Search code examples
boostiterator-facade

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


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 ?


Solution

  • 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.