Search code examples
c++templatesnesteddeclarationfriend

how to declare a global friend function that takes an nested class of a template class?


I have been trying all sorts of declarations but haven't got it right,getting all sorts of errors like syntax errors and linking errors. this is the last attempt that made any sense to me. what am I doing wrong?

template<class T>
class Array
{
public:
  class Iterator
  {
  public:
    friend Iterator operator+<>(const int,typename const Array<T>::Iterator&);
  };
};

template <class T>
typename Array<T>::Iterator operator+(const int,typename const Array<T>::Iterator& it)
{
    return it;
}

and the main:

int main()
{
  Array<int> arr;
  Array<int>::Iterator it;
  it=5+it;
  return 0;
}

i get this error:

 error C2785: 'Array<T>::Iterator operator +(const int,const Array<T>::Iterator &)' and 'Array<T>::Iterator +(const int,const Array<T>::Iterator &)' have different return types

Solution

  • For one thing, typename const Array<T>::Iterator is wrong. It should be const typename Array<T>::Iterator. Since typename is for helping the compiler know what to do with something after a ::, always put it immediately left of your A::B::C -type pattern.

    You can't name a specialization of a function template as a friend before the general template has been declared. And you can't declare that template until the type Array<T>::Iterator has been declared.

    One thing you could do: Make the entire function template a friend, not just the one specialization.

    template<class T>
    class Array
    {
    public:
        class Iterator
        {
        public:
            template <class U> friend
            typename Array<U>::Iterator operator+(
                const int, const typename Array<U>::Iterator&);
        };
    };
    
    template <class T>
    typename Array<T>::Iterator operator+(
        const int, const typename Array<T>::Iterator& it)
    {
        return it;
    }
    

    It's a little bit sloppy in granting friendship, but gets the job done.

    Or, if you're willing to move the definition of nested class Iterator later in the file:

    template<class T>
    class Array
    {
    public:
        class Iterator;
    };
    
    template <class T>
    typename Array<T>::Iterator operator+(
        const int, const typename Array<T>::Iterator& it);
    
    template <class T>
    class Array<T>::Iterator
    {
    public:
        friend Iterator operator+<T>(const int, const Iterator&);
    };
    
    template <class T>
    typename Array<T>::Iterator operator+(
        const int, const typename Array<T>::Iterator& it)
    {
        return it;
    }
    

    That takes care of how to declare and define them. Unfortunately, this operator+ is not easy to use, because of the details of template argument deduction rules....

    I would probably try getting around this last issue by making Iterator a non-nested template:

    namespace Array_detail {
        template <class T> class Array_Iterator;
    
        template <class T>
        Array_Iterator<T> operator+(int, const Array_Iterator<T>&);
    
        template <class T>
        class Array_Iterator {
            friend Array_Iterator operator+<>(int, const Array_Iterator&);
        };
    }
    
    template <class T>
    class Array {
    public:
        typedef Array_detail::Array_Iterator<T> Iterator;
    };