2

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

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只允许你获取一个列表,它只允许你调用(GOOD)const_handle的非修改方法。myObj

问题是 " read" 方法真的很丑 (BAD)。

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

任何想法?

4

2 回答 2

0

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

你可以使用更漂亮的:

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

但是,我确实认为您的问题源于混合两种类型的 constness:集合成员的 constness 与集合本身的 constness。

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

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

似乎不需要围绕 std::list 创建全新的集合类型,除非您从示例中不可见的其他附加功能中获得了很多众所周知的收益。

然后,您可以使添加的功能免费方法以 std::list 句柄作为输入。不是所有的东西都需要一个对象,而且对对象的操作也不一定是成员方法,除非需要访问私有数据。

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

EDIT -----

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

智能指针 const 正确性

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

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);
    }
};
于 2013-07-05T02:58:55.150 回答
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<>具有这样的支持。不过,这可能不会很容易。Aconst_propagating_shared_ptr可能是两者中更容易的一个。它必须封装一个std::shared_ptr<T>对象并将几乎所有内容按原样转发给它。与它相反,std::shared_ptr<T>它会有单独的const和非const版本的operator->,operator*()get().

于 2013-07-05T23:02:44.117 回答