boost::iterator_facade operator->() fails to compile

462 views Asked by At

Consider the following code:

#include <boost/iterator/iterator_facade.hpp>
#include <map>


    // Class implements an stl compliant iterator to access the "sections" stored within a configuration.
    template < typename _Iterator, typename _Reference >
        class Section
        : public boost::iterator_facade<
                     Section< _Iterator, _Reference >,
                     _Iterator,
                     boost::random_access_traversal_tag,
                     _Reference
                 >
        {
        private:
            // Define the type of the base class:
            typedef boost::iterator_facade<
                Section< _Iterator, _Reference >,
                _Iterator,
                boost::random_access_traversal_tag,
                _Reference
            > base_type;

        public:
            // The following type definitions are common public typedefs:
            typedef Section< _Iterator, _Reference >    this_type;
            typedef typename base_type::difference_type difference_type;
            typedef typename base_type::reference       reference;
            typedef _Iterator                           iterator_type;

        public:
            explicit Section( const iterator_type it )
            : m_it( it )
            { }

            // Copy constructor required to construct a const_iterator from an iterator:
            template < typename _U >
                Section( const Section< _U, _Reference > it )
                : m_it( it.m_it )
                { }

        private:
            // The following classes are friend of this class to ensure access onto the private member:
                                                                 friend class boost::iterator_core_access;
            template < typename _Iterator, typename _Reference > friend class Section;

            void increment( ){ ++m_it; }                                                          // Advance by one position.
            void decrement( ){ --m_it; }                                                          // Retreat by one position.
            void advance( const difference_type& n ){ m_it += n };                                // Advance by n positions.
            bool equal( const this_type& rhs ) const{ return m_it == rhs.m_it; }                  // Compare for equality with rhs.
            reference dereference( ) const { return m_it->second; }                               // Access the value referred to.
            difference_type distance_to( const this_type& rhs ) const{ return rhs.m_it - m_it; }  // Measure the distance to rhs.

        private:
            // Current "section" iterator:
            iterator_type m_it;
        };


struct Data
{
    void f( ) const
    { }
};

typedef std::map< int, Data > map_type;

typedef Section< const map_type::const_iterator, const Data& > iterator_type;

map_type g_map;

iterator_type begin( )
{
    return iterator_type( g_map.begin( ) );
}

void main( )
{
    iterator_type i = begin( );

    // i->f( ); // <---   error C2039: 'f' : is not a member of 'std::_Tree_const_iterator<_Mytree>'
    ( *i ).f( );
}

So the iterator facade shall return a reference to Data type. This works well when dereference operator is called but compile fails when operator->() is called. So I am a bit confused because operator->() tries to return a std::map::iterator. Any ideas ?

1

There are 1 answers

1
sehe On BEST ANSWER

The iterator returns an iterator on dereference. To get the f part, you need to dereference twice.

It looks a lot like you misunderstood the meaning of the template arguments to iterator_facade. The second argument is not supposed to be any iterator type (this is what causes all your trouble). Instead you should use it to name your value_type

From the way you specified the dereference operation (and Ref) and wanted to use it in main (i->f()) it looks like you just wanted to iterate the map's values. So, I'd rewrite the whole thing using more descriptive names as well, and here it is, working:

Live On Coliru

#include <boost/iterator/iterator_facade.hpp>
#include <map>

// Class implements an stl compliant iterator to access the "sections" stored within a configuration.
template <typename Map, typename Value = typename Map::mapped_type>
class MapValueIterator : public boost::iterator_facade<MapValueIterator<Map>, Value, boost::random_access_traversal_tag, Value const&> {
  private:
    // Define the type of the base class:
    typedef Value const& Ref;
    typedef boost::iterator_facade<MapValueIterator<Map>, Value, boost::random_access_traversal_tag, Ref> base_type;

  public:
    // The following type definitions are common public typedefs:
    typedef MapValueIterator<Map> this_type;
    typedef typename base_type::difference_type difference_type;
    typedef typename base_type::reference reference;
    typedef typename Map::const_iterator iterator_type;

  public:
    explicit MapValueIterator(const iterator_type it) : m_it(it) {}

    // Copy constructor required to construct a const_iterator from an iterator:
    template <typename U, typename V> MapValueIterator(const MapValueIterator<U,V> it) : m_it(it.m_it) {}

  private:
    // The following classes are friend of this class to ensure access onto the private member:
    friend class boost::iterator_core_access;
    template <typename U, typename V> friend class MapValueIterator;

    void increment()                                        { std::advance(m_it);      } // Advance by one position.
    void decrement()                                        { std::advance(m_it, -1);  } // Retreat by one position.
    void advance(const difference_type &n)                  { std::advance(m_it, n);   } // Advance by n positions.
    bool equal(const this_type &rhs) const                  { return m_it == rhs.m_it; } // Compare for equality with rhs.
    reference dereference() const                           { return m_it->second;     } // Access the value referred to.
    difference_type distance_to(const this_type &rhs) const { return rhs.m_it - m_it;  } // Measure the distance to rhs.

  private:
    // Current iterator:
    iterator_type m_it;
};

#include <iostream>

struct Data {
    void f() const {
        std::cout << __PRETTY_FUNCTION__ << "\n";
    }
};

typedef std::map<int, Data> map_type;

template <typename Map>
MapValueIterator<Map> map_value_iterator(Map const& m) {
    return MapValueIterator<Map>(m.begin());
}


int main() {
    map_type g_map;
    auto i = map_value_iterator(g_map);

    i->f();
}

Which prints the output

void Data::f() const

as you'd expect.

Note that there are numerous places where I implemented the member functions using standard library facilities. Note as well, the iterator "mimics" random access, but it won't have the expected performance characteristics (increment is O(n)).

Final note: I'd recommend against having the implicit conversion constructor. I think you can do without it.


¹ The reference-type should typically be the same (but ref-qualified) except in rare cases where you actually "proxy" the values. This is an advanced topic and rarely should be used.