[Boost.Foreach] error iterating custom wrappers

Hello to everybody. Im using the great library Boost.Foreach and very happy with it, however currently I met some strange issue I want to show you. Im using custom wrappers to iterate over mapped values in std::map<> container. Here is the wrapper: template<typename CONT> struct Select2ndIt { CONT& cont; Select2ndIt(CONT& cont) : cont(cont) {} typedef typename CONT::mapped_type value_type; struct iterator : public std::iterator <typename CONT::iterator::iterator_category, value_type, typename CONT::iterator::distance_type> { typename CONT::iterator it; // typedef typename CONT::iterator::difference_type difference_type; typedef value_type* pointer; typedef value_type& reference; iterator(typename CONT::iterator it) : it(it) {} bool operator==(const iterator& other) const { return it == other.it; } bool operator!=(const iterator& other) const { return it != other.it; } iterator& operator++() { ++it; return *this; } iterator& operator++(int) { it++; return *this; } iterator& operator--() { --it; return *this; } iterator& operator--(int) { it--; return *this; } reference operator*() const { return it->second; } pointer operator->() const { return &it->second; } }; struct const_iterator : public std::iterator <typename CONT::const_iterator::iterator_category, value_type, typename CONT::const_iterator::distance_type> { typename CONT::const_iterator it; // typedef typename CONT::const_iterator::difference_type difference_type; typedef const value_type* pointer; typedef const value_type& reference; const_iterator(typename CONT::const_iterator it) : it(it) {} bool operator==(const const_iterator& other) const { return it == other.it; } bool operator!=(const const_iterator& other) const { return it != other.it; } const_iterator& operator++() { ++it; return *this; } const_iterator& operator++(int) { it++; return *this; } const_iterator& operator--() { --it; return *this; } const_iterator& operator--(int) { it--; return *this; } reference operator*() const { return it->second; } pointer operator->() const { return &it->second; } }; iterator begin() { return iterator(cont.begin()); } iterator end() { return iterator(cont.end()); } const_iterator begin() const { return const_iterator(cont.begin()); } const_iterator end() const { return const_iterator(cont.end()); } }; template<typename CONT> Select2ndIt<CONT> select2nd(CONT& cont) { return Select2ndIt<CONT>(cont); } And usage: std::map<int, unsigned> cont; BOOST_FOREACH(unsigned& value, alg::select2nd(cont)) value = 0; And MSVS 8.0 tells: error C2440: 'initializing' : cannot convert from 'const unsigned int' to 'unsigned int &' Without wrappers I can easily iterate as a non-const value: std::map<int, unsigned> cont; typedef std::pair<const int, unsigned> value_type; BOOST_FOREACH(value_type& value, cont) value.second = 0; works great. I guess my wrappers very close to STL iterators (they even inherit from std::iterator), however some unwanted constness is appearing, so I afraid it is some issue with Boost.Foreach library. I hope for some help or advice about this problem. Thanks. Evgeny.

Sorry for the delayed response. I was traveling last week, and didn't see this. johny5.coder@gmail.com wrote:
Hello to everybody.
Im using the great library Boost.Foreach and very happy with it, however currently I met some strange issue I want to show you.
Im using custom wrappers to iterate over mapped values in std::map<> container. Here is the wrapper:
template<typename CONT> struct Select2ndIt { CONT& cont;
OK, Select2nsIt is a container proxy.
Select2ndIt(CONT& cont) : cont(cont) {}
typedef typename CONT::mapped_type value_type;
struct iterator : public std::iterator <typename CONT::iterator::iterator_category, value_type, typename CONT::iterator::distance_type> { typename CONT::iterator it;
Actually, this is not quite correct. See below.
// typedef typename CONT::iterator::difference_type difference_type; typedef value_type* pointer; typedef value_type& reference;
iterator(typename CONT::iterator it) : it(it) {}
bool operator==(const iterator& other) const { return it == other.it; } bool operator!=(const iterator& other) const { return it != other.it; } iterator& operator++() { ++it; return *this; } iterator& operator++(int) { it++; return *this; }
Oops, that's wrong. Try this. iterator operator++(int) { iterator that(*this); ++it; return that; }
iterator& operator--() { --it; return *this; } iterator& operator--(int) { it--; return *this; }
Ditto for operator--(int)
reference operator*() const { return it->second; } pointer operator->() const { return &it->second; } };
So far so good. (Although you might simplify this code somewhat (and make it more correct) by using an iterator adaptor from the Boost.Iterators library.)
struct const_iterator : public std::iterator <typename CONT::const_iterator::iterator_category, value_type, typename CONT::const_iterator::distance_type> {
<snip> Here's the problem. Although it would seem to be correct to define separate iterator and const_iterator types in your container proxy, it's actually wrong. The const-ness of the iterators should match the const-ness of the underlying container, not the const-ness of the proxy. Consider the different meanings of the following: std::map<int,unsigned> m1; BOOST_FOREACH(unsigned& value, select2nd(m1)) // OK std::map<int,unsigned> const m2; // NOTE CONST HERE BOOST_FOREACH(unsigned const & value, select2nd(m2)) // OK Whether the values in the map are const or not actually has nothing to do with whether the Select2ndIt proxy object is const. It's just a proxy. So you can fix the issue simply by replacing the const_iterator definition with: typedef iterator const_iterator; However, you'll also need to change the definition of your iterator struct. If CONT is const-qualified, iterator should adapt CONT::const_iterator instead of CONT::iterator. HTH, -- Eric Niebler Boost Consulting www.boost-consulting.com
participants (2)
-
Eric Niebler
-
johny5.coder@gmail.com