Search code examples
c++templateslanguage-lawyerfriend

Friend function with a definition - template or non-template?


Suppose we have the following code:

template<class T> struct S;
template<class T> void operator++(S<T>);

template<class T> struct S {
    friend void operator++(S);
};

template<class T>
void operator++(S<T>) {}

int main() {
    S<int> s;
    ++s;
}

This will compile but won't link, because the friend declaration introduces a non-template operator++, that has never been defined.

This FAQ answer reads (bold is mine):

The solution is to convince the compiler while it is examining the class body proper that the operator++ function is itself a template. There are several ways to do this;

The first way is to add <> into the friend declaration, and I'm not considering it here. The second is "to define the friend function within the class body":

template<class T> struct S {
    friend void operator++(S) { }
};

The quote suggests that void operator++(S) is now a function template and not a non-template function. Is it?


Solution

  • It is not a template, because its declaration is not that of a template (even though it appears inside a template declaration itself).

    [temp.friend] (emphasis mine)

    1 A friend of a class or class template can be a function template or class template, a specialization of a function template or class template, or a non-template function or class. For a friend function declaration that is not a template declaration:

    • if the name of the friend is a qualified or unqualified template-id, the friend declaration refers to a specialization of a function template, otherwise,

    • if the name of the friend is a qualified-id and a matching non-template function is found in the specified class or namespace, the friend declaration refers to that function, otherwise,

    • if the name of the friend is a qualified-id and a matching function template is found in the specified class or namespace, the friend declaration refers to the deduced specialization of that function template ([temp.deduct.decl]), otherwise,

    • the name shall be an unqualified-id that declares (or redeclares) a non-template function.

    [ Example:

    template<class T> class task;
    template<class T> task<T>* preempt(task<T>*);
    
    template<class T> class task {
      friend void next_time();
      friend void process(task<T>*);
      friend task<T>* preempt<T>(task<T>*);
      template<class C> friend int func(C);
    
      friend class task<int>;
      template<class P> friend class frd;
    };
    

    Here, each specialization of the task class template has the function next_­time as a friend; because process does not have explicit template-arguments, each specialization of the task class template has an appropriately typed function process as a friend, and this friend is not a function template specialization; because the friend preempt has an explicit template-argument T, each specialization of the task class template has the appropriate specialization of the function template preempt as a friend; and each specialization of the task class template has all specializations of the function template func as friends. Similarly, each specialization of the task class template has the class template specialization task<int> as a friend, and has all specializations of the class template frd as friends.  — end example ]

    While examples are non-normative, the one in the quote clarifies the intent of the preceding normative text. Since the friend operator declaration is not a template declaration, the text in bold applies. It therefore declares a non-template function.