stl容器中的C ++ 11 shared_pointer constness

时间:2013-07-04 19:25:41

标签: c++ c++11 stl const shared-ptr

我有以下问题,我想知道是否有更好的解决方法:

class myObj {
public:
    typedef std::shared_ptr<myObj> handle;
    typedef std::shared_ptr<const myObj> const_handle;
    int someMethod() { ... }
    int someConstMethod() const { ... }
};

现在我需要的是一个容器类,以某种方式允许你修改或读取myObj的集合,具体取决于它自己的const,如下所示:

class myCollection {
public:
    typedef std::list<myObj::handle> objList;
    typedef std::list<myObj::const_handle> const_objList;

    inline objList& modify() { return _obl; }

    // it would be nice to do this, but it won't compile as 
    // objList and const_objList are completely different types
    inline const_objList& read() const { return _obl; } // doh! compile error...

    // returning a const objList won't help either as it would return non-const
    // handles, obviously.

    // so I am forced to do this, which sucks as i have to create a new list and copy
    void read(const_objList &l) {
        std::for_each(
            _obl.begin(), 
            _obl.end(), 
            [&l] (myObj::handle &h) { l.push_back(h); } 
            // ok as handle can be cast to const_handle
        ); // for_each
    }

private:
    objList _obl;
};

因此,此解决方案实际上可以作为const myCollection使用,只允许您获取const_handle的列表,该列表仅允许您调用myObj(GOOD)的非修改方法。< / p>

问题是“read”方法真的很难看(BAD)。

另一种方法是以某种方式公开list方法并返回const_handle并根据需要处理,但这会产生很多开销,特别是如果你想使用比列表更复杂的东西。

有什么想法吗?

2 个答案:

答案 0 :(得分:0)

鉴于你所说的你想要完成的事情,我认为你的解决方案太糟糕了。想象一下,其他一些代码可能正在修改内部集合,例如添加或删除值。返回集合的当前状态的副本对于客户端代码是安全的,因为它可以在副本上工作,同时没有元素被删除的危险。但我离题了,这涉及到线程问题,可能不相关。

你可以使用更漂亮:

  inline const_objList read()  const
   { 
       const_objList cl(_obl.begin(), _obl.end());
       return cl; 
   } 

但是,我确实认为你的问题来自混合两种类型的const:集合成员的constness与集合本身的constness。

而不是整个列表处理的Modify和Read方法,我会尝试将const和非const迭代器暴露给内部列表,通过相应的const和非const方法返回所述迭代器。

但这立即引出了一个问题:为什么首先要myCollection

似乎不需要在std :: list周围创建全新的集合类型,除非你从其他增加的功能中获得了大量的谚语,这些功能在您的示例中是不可见的。

然后,您可以使用自动添加的功能方法,将std :: list作为输入。并非一切都需要对象,对象上的操作不一定是成员方法,除非需要访问私有数据。

您提到可能使用其他容器而不是列表。但是你的类不会这样做,除非你有一个模板,其中template参数可以是STL容器之一。 这意味着你应该暴露迭代器。 也就是说,如果您预见到更改内部集合类型,您可能希望使myCollection的公共接口对集合类型透明。否则,每次您改变对内部实现的想法时,客户端都必须重新编译。

EDIT -----

最后,如果实现迭代器(虽然有趣且最正确)太多了,为什么不去像这个SO帖子中的简单getter:

smart pointer const correctness

我将引用RüdigerStevens的最高答案(它假设向量而不是列表):

template <typename T>
class MyExample
{
  private:
    vector<shared_ptr<T> > data;

  public:
    shared_ptr<const T> get(int idx) const
    {
      return data[idx];
    }
    shared_ptr<T> get(int idx)
    {
      return data[idx];
    }
    void add(shared_ptr<T> value)
    {
      data.push_back(value);
    }
};

答案 1 :(得分:0)

指向T的指针列表不是指向常量T的指针列表。

std::list<std::shared_ptr<int>> a;
std::list<std::shared_ptr<const int>>& ra = a; // illegal but imagine it's not
std::shared_ptr<const int> x = std::make_shared<const int>(42);
ra.push_back(x); // totally legal, right?
++**a.begin(); // oops... just incremented a const int

现在,指向T的列表是概念上常量指针到常量T的常量列表,但是std::list<std::shared_ptr<T>>不支持这种深度const传播。 const std::list<std::shared_ptr<T>>包含指向非常量对象的常量指针。

您可以编写自己的list<> 变体或您自己的shared_ptr<>变体,并获得此类支持。但这可能不会那么容易。 const_propagating_shared_ptr可能更容易。它必须封装一个std::shared_ptr<T>对象,并将几乎所有内容转发给它。与std::shared_ptr<T>相反,它会分别为constconstoperator->的{​​{1}}和非operator*()版本。