Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly declare a friend of a nested class of a template class?

When I do the following:

template <typename T>
class Container
{
public:
    class Iterator
    {
        friend bool operator==(const Iterator& x, const Iterator& y);
    };
};

gcc gives me the following warning and suggestion:

warning: friend declaration 
'bool operator==(const Container<T>::Iterator&, 
                 const Container<T>::Iterator&)' 
declares a non-template function [-Wnon-template-friend]

friend bool operator==(const Iterator& x, const Iterator& y);
                                                           ^

(if this is not what you intended, 
 make sure the function template has already been declared 
 and add <> after the function name here)

I am fairly sure that this is a new warning, since I have always done it like this and never had any problems.

Can someone please explain why is this a warning, and what it warns about?

like image 688
Martin Drozdik Avatar asked Mar 11 '15 22:03

Martin Drozdik


2 Answers

It's warning about the fact that it is going to be virtually impossible to define that operator== out-of-class.

That is to say, that friend declaration befriends a non-template operator== function - for example, Container<Int>::Iterator has as a friend the function

bool operator==(const Container<Int>::Iterator&, const Container<Int>::Iterator&);

This function is not a template, so there's pretty much no way to define operator== for all possible Containers outside the class template definition.

If you try to do

template<class T>
bool operator==(const Container<T>::Iterator&, const Container<T>::Iterator&);

That's a function template, and doesn't match the friend declaration. (In this case it's even worse, as you can't actually use this operator because T is in a non-deduced context.)

The warning message suggests one possible fix - first declaring a function template and then befriending a specialization of it. (You'll need to pull Iterator out of the class into its own separate class template so that T can be deduced.) The other possible fix is to just define the function inside the class template definition.

like image 172
T.C. Avatar answered Oct 17 '22 23:10

T.C.


The declaration

friend bool operator==(const Iterator& x, const Iterator& y);

declares a non-template function at the nearest enclosing namespace scope which takes two arguments of type const typename Container<T>::Iterator&. Thus, every time the class Container is instantiated, with some template parameter T, a new overload of operator== is declared.

Since this operator== is not instantiated from a template, you cannot define it out-of-line as a template. So the only way to define this operator== you have declared is to define it separately for each type for which Container is instantiated. This is almost certainly not desirable.

The warning warns you that the friend you declared is not a template, which has the undesirable consequences I just explained.

I believe the correct way to do what you are trying to do is the following:

template <typename T>
class Container_Iterator;

template <typename T> bool operator==(const Container_Iterator<T>& x,
                                      const Container_Iterator<T>& y);

template <typename T>
class Container
{
public:
    typedef Container_Iterator<T> Iterator;
};

template <typename T>
class Container_Iterator {
  friend bool operator==<T>(const Container_Iterator<T>& x,
                            const Container_Iterator<T>& y);
};

// btw, don't forget to define operator==

Now we explicitly declare operator== as a template, and each specialization of the iterator declares the corresponding specialization of operator== as its friend.

like image 4
Brian Bi Avatar answered Oct 17 '22 23:10

Brian Bi