c++c++11iteratoriterator-traits

How to avoid specializing iterator_traits for each possible instantiation of a templated iterator?


I want to pimp up my ranged based for loops, for example by enabling reversed iteraton. I managed to get it working to some degree by writing an adaptor, but I am lost on how to make the adpaptor composable.

#include <iostream>
template <typename IT> struct reversed_range {
        struct reversed_iterator {
            IT it;
            reversed_iterator(IT it): it(it){}
            reversed_iterator& operator++(){
                --it;
                return (*this);
            }
            typename std::iterator_traits<IT>::reference operator*(){
                IT tmp = it;
                --tmp;
                return *tmp;
            }
            bool operator==(const reversed_iterator& other) const {
                return it == other.it;
            }
            bool operator!=(const reversed_iterator& other) const { 
                return !(*this == other);
            }
        };
        IT itbegin;
        IT itend;
        reversed_range(const IT& b,const IT& e): itbegin(b),itend(e){}
        reversed_iterator begin() const { return reversed_iterator(itend); }
        reversed_iterator end() const { return reversed_iterator(itbegin); }
    };
    template <typename IT>
    reversed_range<IT> reverse_range(const IT& begin,const IT& end) { 
        return reversed_range<IT>(begin,end); 
    }
    template <typename C>
    reversed_range<C> reverse_range(const C& c) {
        return reversed_range<typename C::iterator>(std::begin(c),std::end(c));
    }

int main() {
    int x[] = {1,2,3,4,5};
    for (auto y : reverse_range(std::begin(x),std::end(x))){
        std::cout << y << "\t";
    }
    for (auto y : reverse_range(reverse_range(std::begin(x),std::end(x)))){
        std::cout << y << "\t";
    }
    return 0;
}

The first loop works like a charm, but for the second I get the error:

error: no type named ‘reference’ in ‘struct std::iterator_traits<reversed_range<int*> >’
             typename std::iterator_traits<IT>::reference operator*(){
                                                          ^~~~~~~~

I know why this happens and I know how I could fix it for this particular case. However, if i correclty understand this answer I would have to specialize iterator_traits for each possible instantiation of my reversed_iterator which isnt really feasible. I am a bit lost, most of the time I write my own iterators I find myself writing lots of boilerplate just to reach a point where I realize that I would need exponentially more boilerplate to get it working. I mean I didnt even start to consider const_iterators.

Is there a way to get the above working (whithout having to specialize iterator_traits for each iterator I ever want to reverse?

PS: tagged as C++11, because thats my current scope, but if there are improvements with respect to this, I wouldnt mind to use a newer standard.


Solution

  • std::iterator_traits<Iterator>::something is simply Iterator::something by default. Thus, simply add the typedefs into your reversed_iterator type:

    struct reversed_iterator {
        using difference_type = typename std::iterator_traits<IT>::difference_type;
        using value_type = typename std::iterator_traits<IT>::value_type;
        using pointer = typename std::iterator_traits<IT>::pointer;
        using reference = typename std::iterator_traits<IT>::reference;
        using iterator_category = /* appropriate category */;
    
        // ...
    };