2013-10-27 143 views
9

我试图定义我的软件,这意味着并注意一些变量读/写访问一个好的设计。我在这里简化了讨论的计划。希望这对其他人也有帮助。 :-)的shared_ptr <T>到shared_ptr <T const>和矢量<T>到矢量<T const>

比方说,我们有一个类X如下:

class X { 
    int x; 
public: 
    X(int y) : x(y) { } 
    void print() const { std::cout << "X::" << x << std::endl; } 
    void foo() { ++x; } 
}; 

我们也可以说,在未来这一类将与X1,X2,被继承...可以重新实现print()foo() 。 (我省略了需要virtual关键字为简单起见在这里,因为这不是我所面对的实际问题。)

因为我们将用多态性研究,让我们使用(智能)指针和定义一个简单的工厂:

using XPtr = std::shared_ptr<X>; 
using ConstXPtr = std::shared_ptr<X const>; 

XPtr createX(int x) { return std::make_shared<X>(x); } 

到现在为止,一切都很好:我可以定义goo(p),它可以读取和写入phoo(p),它只能读取p

void goo(XPtr p) { 
    p->print(); 
    p->foo(); 
    p->print(); 
} 

void hoo(ConstXPtr p) { 
    p->print(); 
// p->foo(); // ERROR :-) 
} 

而调用点看起来像这样:

XPtr p = createX(42); 

    goo(p); 
    hoo(p); 

共享指针X(XPtr)被自动地转换到其const版本(ConstXPtr。很好,这正是我想要的!

现在来麻烦了:我需要一个异构集合X。我的选择是std::vector<XPtr>。 (它也可能是list,为什么不)。

我想到的设计是以下几点。我有两个版本的容器:一个对其元素具有读/写访问权限,另一个对其元素进行只读访问。

using XsPtr = std::vector<XPtr>; 
using ConstXsPtr = std::vector<ConstXPtr>; 

我有处理这个数据的类:

class E { 
    XsPtr xs; 
public: 
    E() { 
     for (auto i : { 2, 3, 5, 7, 11, 13 }) { 
      xs.emplace_back(createX(std::move(i))); 
     } 
    } 

    void loo() { 
     std::cout << "\n\nloo()" << std::endl; 
     ioo(toConst(xs)); 

     joo(xs); 

     ioo(toConst(xs)); 
    } 

    void moo() const { 
     std::cout << "\n\nmoo()" << std::endl; 
     ioo(toConst(xs)); 

     joo(xs); // Should not be allowed 

     ioo(toConst(xs)); 
    } 
}; 

ioo()joo()功能如下:

void ioo(ConstXsPtr xs) { 
    for (auto p : xs) { 
     p->print(); 
//  p->foo(); // ERROR :-) 
    } 
} 

void joo(XsPtr xs) { 
    for (auto p: xs) { 
     p->foo(); 
    } 
} 

正如你所看到的,在E::loo()E::moo()我必须做一些转换toConst()

ConstXsPtr toConst(XsPtr xs) { 
    ConstXsPtr cxs(xs.size()); 
    std::copy(std::begin(xs), std::end(xs), std::begin(cxs)); 
    return cxs; 
} 

但是,这意味着遍地....复制的一切: -/

此外,在moo(),这是常量,我可以叫joo()将修改xs的数据。不是我想要的。在这里,我宁愿编译错误。

完整的代码在ideone.com

问题是:是否有可能做到这一点,但没有将矢量复制到它的const版本?或者更一般地说,是否有一种既高效又易于理解的良好技术/模式?

谢谢。 :-)

+0

获取'boost :: adapters :: transformed'的'const'-view和一个appropri吃了功能对象来转换你的共享指针。 – Xeo

+0

@Xeo:我很快看了'boost :: adapters :: transformed',但是似乎我必须在某些时候复制东西,有点像上面的但有不同的语法,对吧?如果情况并非如此,您是否介意在下面举个例子? :-) – Hiura

+0

只是一个说明。你的'std :: move(i)'不会移动任何东西。 “移动”不移动,它只是一个演员。也许这只是从你的实际代码复制到它的位置:) – typ1232

回答

0

根据评论和答案,我最终创建了容器视图。

基本上我定义了新的迭代器。我在github上创建一个项目:mantognini/ContainerView

的代码可能可以改善,但其主要思想是具有两个模板类,ViewConstView,具有用于迭代底层容器上的begin()end()方法现有容器(例如std::vector<T>)上。

通过一点点继承(ViewConstView),它有助于在需要时将读写转换为只读视图,而无需额外的代码。

由于我不喜欢指针,因此我使用模板特化来隐藏std::shared_ptr:在std::shared_ptr<T>的容器上的视图不需要额外的解引用。 (我还没有实现它的原始指针,因为我没有使用它们。)

Here is a basic example of my views in action.

6

我认为通常的回答是,对于一个类模板X<T>,任何X<const T>可以专业,因此编译器不允许简单地假设它可以将指针或X<T>基准转换为X<const T>,并且有不是一般的表达方式,这两者实际上是可转换的。但是,然后我虽然:等等,有一种方式可以说X<T>IS AX<const T>IS A通过继承来表达。

尽管这对std::shared_ptr或标准容器没有帮助,但在实现自己的类时可能需要使用这种技术。事实上,我想知道std::shared_ptr和集装箱是否可以/应该改进以支持这一点。任何人都可以看到这个问题吗?

我心目中的技术将这样的工作:

template< typename T > struct my_ptr : my_ptr< const T > 
{ 
    using my_ptr< const T >::my_ptr; 
    T& operator*() const { return *this->p_; } 
}; 

template< typename T > struct my_ptr< const T > 
{ 
protected: 
    T* p_; 

public: 
    explicit my_ptr(T* p) 
     : p_(p) 
    { 
    } 

    // just to test nothing is copied 
    my_ptr(const my_ptr& p) = delete; 

    ~my_ptr() 
    { 
     delete p_; 
    } 

    const T& operator*() const { return *p_; } 
}; 

Live example

+0

你是否试图编译它? – 2013-10-27 15:09:09

+0

@typical是的,请参阅答案中的“Live example”链接,随时随地使用它。 –

+1

'的std :: shared_ptr的'可以通过*转换为它的const版本*'模板< class Y > shared_ptr的(常量的shared_ptr & r);'。这就是我的问题的第一部分以'咕发生()'和'HOO( )''所以我想我不必为共享指针类重新创建轮子,对吧? – Hiura

1

有一个根本性的问题与你想要做什么。

一个std::vector<T const*>不是std::vector<T*>的限制,而同样是含有智能指针及其const版本vector真的。

具体而言,我可以在第一个容器中存储指向const int foo = 7;的指针,但不是第二个。 std::vector既是范围又是容器。它类似于T** vs T const**问题。

现在,在技术上std::vector<T const*> conststd::vector<T>的限制,但不支持。

解决此问题的一种方法是启动workimg eith范围视图:非拥有其他容器的视图。一个非拥有T const*迭代器视图到std::vector<T *>是可能的,并且可以给你你想要的接口。

boost::range可以为你做样板,但是自己写contiguous_range_view<T>random_range_view<RandomAccessIterator>并不难。当你想要自动检测迭代器类别并启用基于此的功能时,它会变得很花哨,这就是为什么boost::range包含更多代码。

+0

你能详细介绍一下这些boost :: range吗?也许通过展示如何使用ioo和joo来使用这些以及调用网站会是什么样子?谢谢。 – Hiura

+0

如果我正确读取boost文档,'std :: vector '替换为'boost :: iterator_range :: const_iterator>'。 – Yakk

1

Hiura,

我试图从回购和g ++ 4.8编译代码返回了一些错误。 main.cpp中的更改:97,其余行以lambda函数调用view :: create()作为第二个参数。 +加+

auto f_lambda([](view::ConstRef_t<view::ElementType_t<Element>> const& e) { return ((e.getX() % 2) == 0); }); 

std::function<bool(view::ConstRef_t<view::ElementType_t<Element>>)> f(std::cref(f_lambda)); 

+ MOD +

printDocument(view::create(xs, f)); 

也View.hpp:185需要额外的操作,即: +加+

bool operator==(IteratorBase const& a, IteratorBase const& b) 
{ 
    return a.self == b.self; 
} 

BR, 马立克Szews

+0

感谢您的支持,但您应该在Github上打开一个问题,而不是在这里回答 - 如果您想遵循SO的精神。 – Hiura

相关问题