我有以下问题,我想知道是否有更好的解决方法:
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
并根据需要处理,但这会产生很多开销,特别是如果你想使用比列表更复杂的东西。
有什么想法吗?
答案 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>
相反,它会分别为const
,const
和operator->
的{{1}}和非operator*()
版本。