c++ - C++11 cast const iterator pointing to container of shared_ptr objects -
i have stl container element type const std::shared_ptr<myclass>.
i want supply 2 iterator types user:
mycontainer::iterator
typedefed std::vector<const std::shared_ptr<myclass>>::iterator (which should same type std::vector<const std::shared_ptr<const myclass>>::const_iterator
mycontainer::const_iterator
typedefed std::vector<const std::shared_ptr<const myclass>>::iterator (which should same type std::vector<const std::shared_ptr<const myclass>>::const_iterator
in other words, want "const" refer myclass constness, not shared_ptr constness. solution found getting second iterator type getting first one, easy (e.g. using vector::begin), , converting second type using static_cast (fixme: no need use const_cast because i'm adding constness, not removing it).
would common good-design way achieve that, or there's better/more common way?
typedefed
std::vector<const std::shared_ptr<myclass>>::iterator(which should same typestd::vector<std::shared_ptr<const myclass>>::const_iterator
but isn't same type. iterators not pointers. if iterator , const_iterator types defined inside vector unrelated types:
template<typename t> class vector { class iterator; class const_iterator; // ... vector<const int> different type vector<int> , nested types different. far compiler concerned unrelated types, i.e. cannot move const around point in type , compatible types:
vector<const shared_ptr<const t>>::iterator you cannot use const_cast convert between unrelated types. can use static_cast convert vector<t>::iterator vector<t>::const_iterator it's not cast, you're constructing latter former, allowed because conversion required standard.
you can convert shared_ptr<const t> shared_ptr<t> const_pointer_cast<t> again because it's defined work standard, not because types inherently compatible , not because "just works" plain ol' pointers.
since vector's iterators don't provide deep-constness want, you'll need write own, it's not hard:
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()); } // ... }; that iterator type mising few things (operator--, operator+) they're easy add, following same ideas shown.
the key point notice in order const_iterator::operator* return reference, there needs shared_ptr<const myclass> object stored member of iterator. member acts "cache" shared_ptr<const myclass> value, because underlying container's real elements different type, shared_ptr<myclass>, need somewhere cache converted value reference can returned. n.b. doing breaks iterator requirements, because following doesn't work expected:
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! the reason assertion fails *ci doesn't return reference underlying element of container, member of iterator, gets modified following increment , dereference. if behaviour isn't acceptable you'll need return proxy iterator instead of caching value. or return shared_ptr<const myclass> when const_iterator dereferenced. (the difficulties of getting 100% right 1 of reasons stl containers don't try model deep constness!)
a lot of effort of defining own iterator types done boost::iterator_adaptor utility, example above useful exposition. adaptor you'd need own custom iterator types desired behaviour:
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) { } };
Comments
Post a Comment