While answering to a different question on SO, I came across a somewhat suspicious compiler error with gcc. The offending snippet is
template <class T> class A;
template <class T, class U>
void operator*(A<T>, A<U>);
template <class T>
class A {
friend void ::operator*(A<T>, A<T>);
...
whose last line gives the famous warning
friend declaration ‘
void operator*(A<T>, A<T>)‘ declares a
non-template function
leading to hard errors later. The full code can be found here.
Now, the problem is I don’t think the behavior is appropriate. The standard in [temp.friend]/1 says:
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 nontemplate 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 specialization of a template function is found
in the specified class or namespace, the friend declaration refers to that function specialization, otherwise,
this is C++03; C++11 contains similar clause
A specialization of a template is defined by [temp.spec]/4:
… A specialization is a class, function, or class member that is either
instantiated or explicitly specialized (14.7.3).
and [temp.fct.spec]/1:
A function instantiated from a function template is called a function template specialization; so is an
explicit specialization of a function template. Template arguments can either be explicitly specified …
[temp.arg.explicit]/2 says this about specifying a template argument list for a function specification:
A template argument list may be specified when referring to a specialization of a function template
…
— in a friend declaration.
Trailing template arguments that can be deduced (14.8.2) may be omitted from the list of explicit template-arguments. If all of the template arguments can be deduced, they may all be omitted; in this case, the
empty template argument list <> itself may also be omitted.
So, by [temp.fct.spec]/1, ::operator*<T,T>(A<T>, A<T>) is a function template specialization; and since the template parameters can be deduced, it can be referred to as ::operator*(A<T>, A<T>). So I conclude the qualified-id in the friend declaration denotes a function template specialization.
I think that the emphasized condition is fulfilled; therefore, the friend declaration should befriend the class with the operator template (implicit) specialization. However, gcc thinks otherwise and goes on to the fourth bullet which, only concerns friends designated by unqualified-ids, even though the friend is actually named by a qualified-id.
Is my interpretation correct or is gcc right in this case?
I believe gcc is correct.
First the current wording:
From [14.8.2.6 Deducing template arguments from a function declaration]:
In your case, template argument deduction is not performed because the declarator-id does not refer to a specialization. I think the important part is
whose declarator-id refers to a specializationas the condition for this to happen. Simply put, you need the<>for the first sentence in14.8.2.6p1to happen (if I am reading this correctly).UPDATE
Let’s break down what a declarator-id is for this situation:
As seen from the above grammar,
void ::operator*(A<T>, A<T>)is a:: operator-function-idand not a:: template-id. What this means is the syntax can never declare a template function (as mentioned in the error message). For it to be a template-id you have to useoperator-function-id < template-argument-listopt>syntax.