2
votes

I have a template class with a nested template custom iterator (specialized into const/non-const iterators) like this :

template <typename T>
struct A
{
    template <typename U>
    struct AIterator
    {
        //...
    };

    typename AIterator<T*> iterator;
    typename AIterator<const T*> const_iterator;
};

template <typename T>
bool operator==(const typename A<T>::iterator& lhs,
                const typename A<T>::iterator& rhs,)
{
    //...
}

template <typename T>
bool operator!=(const typename A<T>::iterator& lhs,
                const typename A<T>::iterator& rhs,)
{
    //...
}

//idem for const_iterator...

But clang can't infer the template parameter :

snake_test.cpp:17:68: error: invalid operands to binary expression ('wavelet::Snake<float>::const_iterator' (aka 'Iterator<const float *>') and 'const_iterator' (aka 'Iterator<const float *>'))
        for (wavelet::Snake<float>::const_iterator it = snake.begin(); it != snake.end(); it++)
                                                                       ~~ ^  ~~~~~~~~~~~
./snake.hpp:150:6: note: candidate template ignored: couldn't infer template argument 'T'
bool operator!=(const typename Snake<T>::iterator& lhs,
     ^
./snake.hpp:164:6: note: candidate template ignored: couldn't infer template argument 'T'
bool operator!=(const typename Snake<T>::const_iterator& lhs,
     ^
1 error generated.

What am I doing wrong ? How to properly implement custom iterators for template classes ?

1
For one, your reference tags are on the wrong side of your arguments. ex: lhs& should be &lhs, etc. The trailing commas after the rhs arguments aren't helping much either. - WhozCraig
Thanks ! I was cooking while writing this question so I wrote it fast therefore it could remain some typos. ;-) - matovitch
Have you tried defining operator== and operator!= inside of A? - More Axes
Template parameters in a nested-name-specifier of a function template function parameter are in a non-deduced context. I.e. in A<T>::iterator, T cannot be deduced. - dyp
@matovitch Binary operators (two operands) are implemented as non-member functions because of symmetry. This allows identical conversion for the LHS and RHS (consider struct BigInt { BigInt(int); }; BigInt b; 42 == b) and is "cleaner" because typically such operators are commutative anyway, meaning LHS and RHS have the same significance. - dyp

1 Answers

4
votes

The possibly easiest and cleanest solution is to use non-member friend functions defined inside the class body:

template <typename T>
struct A
{
    template <typename U>
    struct AIterator
    {
        friend bool operator==(AIterator const& lhs, AIterator const& rhs)
        { /* implement here */ }
    };

    typename AIterator<T*> iterator;
    typename AIterator<const T*> const_iterator;
};

This will create a non-member function for each specialization of AIterator. You cannot provide the definition outside for this non-member function, as far as I know -- it is not a function template, but a proper function for each specialization. So you can only define a fixed set of specializations in the global namespace.