我有一个STL容器,其元素类型为const std::shared_ptr<MyClass>
.
我想向用户提供两种迭代器类型:
-
MyContainer::iterator
类型定义为 std::vector<const std::shared_ptr<MyClass>>::iterator
(应该与 std::vector<const std::shared_ptr<const MyClass>>::const_iterator
类型相同
-
MyContainer::const_iterator
类型定义为 std::vector<const std::shared_ptr<const MyClass>>::iterator
(应该与 std::vector<const std::shared_ptr<const MyClass>>::const_iterator
类型相同
换句话说,我希望“const
”引用MyClass
常量,而不是shared_ptr
恒定性。我找到的获取第二个迭代器类型的解决方案是获取第一个迭代器类型,这很容易(例如使用 vector::begin
),然后使用 static_cast
将其转换为第二种类型。 (修复我:不需要使用 const_cast
因为我添加了常量,而不是删除它)。
这是否是实现这一目标的常见良好设计方法,或者有更好/更常见的方法?
最佳答案
typedefed as
std::vector<const std::shared_ptr<MyClass>>::iterator
(which should be the same type asstd::vector<std::shared_ptr<const MyClass>>::const_iterator
但它可能不是同一类型。迭代器不仅仅是指针。如果iterator
和const_iterator
类型在 vector
内定义那么它们是完全不相关的类型:
template<typename T>
class vector
{
class iterator;
class const_iterator;
// ...
vector<const int>
与 vector<int>
是不同的类型所以它们的嵌套类型也不同。就编译器而言,它们是完全不相关的类型,即您不能只移动 const
围绕此类型中的任何点并获取兼容类型:
vector<const shared_ptr<const T>>::iterator
您不能使用const_cast
在不相关的类型之间进行转换。您可以使用static_cast
转换 vector<T>::iterator
到 vector<T>::const_iterator
但这并不是真正的强制转换,您是从前者构造后者,这是允许的,因为标准需要该转换。
您可以转换 shared_ptr<const T>
到 shared_ptr<T>
与 const_pointer_cast<T>
但这只是因为它被定义为按标准工作,而不是因为类型本质上兼容,也不是因为它像普通的指针一样“正常工作”。
自 vector
的迭代器不提供您想要的深度常量,您需要编写自己的迭代器,但这并不难:
class MyClass { };
class MyContainer
{
typedef std::vector<std::shared_ptr<MyClass>> container_type;
container_type m_cont;
public:
typedef container_type::iterator iterator;
class const_iterator
{
typedef container_type::const_iterator internal_iterator;
typedef std::iterator_traits<internal_iterator> internal_traits;
const_iterator(internal_iterator i) : m_internal(i) { }
friend class MyContainer;
public:
const_iterator() { }
const_iterator(iterator i) : m_internal(i) { }
typedef std::shared_ptr<const MyClass> value_type;
typedef const value_type& reference;
typedef const value_type* pointer;
typedef internal_traits::difference_type difference_type;
typedef internal_traits::iterator_category iterator_category;
const_iterator& operator++() { ++m_internal; return *this; }
const_iterator operator++(int) { const_iterator tmp = *this; ++m_internal; return tmp; }
reference operator*() const { m_value = *m_internal; return m_value; }
pointer operator->() const { m_value = *m_internal; return &m_value; }
// ...
private:
internal_iterator m_internal;
mutable value_type m_value;
};
iterator begin() { return m_cont.begin(); }
const_iterator begin() const { return const_iterator(m_cont.begin()); }
// ...
};
该迭代器类型缺少一些东西( operator--
、 operator+
),但它们很容易添加,遵循与已经显示的相同的想法。
需要注意的关键点是,为了 const_iterator::operator*
要返回引用,需要有 shared_ptr<const MyClass>
对象存储为迭代器的成员。该成员充当 shared_ptr<const MyClass>
的“缓存”值,因为底层容器的实际元素是不同的类型,shared_ptr<MyClass>
,因此您需要在某个地方缓存转换后的值,以便可以返回对其的引用。注意:这样做会稍微破坏迭代器的要求,因为以下内容无法按预期工作:
MyContainer::const_iterator ci = c.begin();
const shared_ptr<const MyClass>& ref = *ci;
const MyClass* ptr = ref.get();
++ci;
(void) *ci;
assert( ptr == ref.get() ); // FAIL!
断言失败的原因是*ci
不返回对容器底层元素的引用,而是返回对迭代器成员的引用,该成员会通过以下增量和取消引用进行修改。如果这种行为 Not Acceptable ,您需要从迭代器返回代理而不是缓存值。或者返回shared_ptr<const MyClass>
当 const_iterator
被取消引用。 (获得 100% 正确的困难是 STL 容器不尝试对深度常量进行建模的原因之一!)
定义您自己的迭代器类型的许多工作都是由 boost::iterator_adaptor
为您完成的。实用程序,因此上面的示例仅对说明有用。使用该适配器,您只需执行此操作即可获得具有所需行为的自定义迭代器类型:
struct iterator
: boost::iterator_adaptor<iterator, container_type::iterator>
{
iterator() { }
iterator(container_type::iterator i) : iterator_adaptor(i) { }
};
struct const_iterator
: boost::iterator_adaptor<const_iterator, container_type::const_iterator, std::shared_ptr<const MyClass>, boost::use_default, std::shared_ptr<const MyClass>>
{
const_iterator() { }
const_iterator(iterator i) : iterator_adaptor(i.base()) { }
const_iterator(container_type::const_iterator i) : iterator_adaptor(i) { }
};
关于C++11 强制转换const迭代器指向shared_ptr对象的容器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15164330/