I would like to write a class template having a friend function template. If I write
template <typename T>
class A {
friend void f<>(A&);
int i = 0;
};
template <typename T>
void f(A<T>& a) {
a.i = 1;
}
then it is totally fine for GCC and MSVC compilers.
But Clang dislikes it, complaining:
error: no candidate function template was found for dependent friend function template specialization
and requiring me to forward declare function template f
and so class template A
before its definition. Online demo: https://gcc.godbolt.org/z/j7Kb97vKs
Which implementation is correct here?
In current C++, the code above is not legal. It's possible, though I've not checked, that the standard may have been unclear in the past about whether this code was legal.
The meaning of the friend declaration is governed by [dcl.meaning.general]/2.2 (added by P1787R6) because the declarator-id of the friend declaration is A<>
, which is a template-id:
If the id-expression E in the declarator-id of the declarator is a qualified-id or a template-id:
- If the friend declaration is not a template declaration, then in the lookup for the terminal name of E:
- if the unqualified-id in E is a template-id, all function declarations are discarded;
- otherwise, if the declarator corresponds ([basic.scope.scope]) to any declaration found of a non-template function, all function template declarations are discarded;
- each remaining function template is replaced with the specialization chosen by deduction from the friend declaration ([temp.deduct.decl]) or discarded if deduction fails.
- The declarator shall correspond to one or more declarations found by the lookup; they shall all have the same target scope, and the target scope of the declarator is that scope.
The friend declaration is not a template declaration, so the terminal name f
is looked up, deduction is done for each function template found by the lookup, and void f<>(A&)
must correspond to at least one of the resulting instantiated declarations.
The big question, then, is where the lookup is done from. This is governed by [temp.res.general]/1:
A name that appears in a declaration D of a template T is looked up from where it appears in an unspecified declaration of T that either is D itself or is reachable from D and from which no other declaration of T that contains the usage of the name is reachable. If the name is dependent (as specified in [temp.dep]), it is looked up for each specialization (after substitution) because the lookup depends on a template parameter.
This tells us the general rule that when name lookup is done for a name that appears in a template, it's done from the point where the name appears, not the point of instantiation of the template. If the name lookup is for the function name in a call that uses argument-dependent lookup, there is a special rule ([basic.lookup.argdep]/4) that causes the argument-dependent portion of the lookup to be done from the point of instantiation as well. (There is also a gap in the wording, because when looking up class members, you should be able to find them even if the class definition is after the definition context as long as it's reachable from the instantiation context; see CWG2926.)
But the lookup for f
in the friend declaration is not subject to any of these special rules, so it is ordinary unqualified name lookup and must find something that was declared before the point where f
appears.
When is that name lookup actually done: before or after A
is instantiated? According to [temp.res.general]/1, it's done at the template definition time if the name is non-dependent, and at instantiation time if the name is dependent. Because f
is an unqualified name, it is dependent only under the conditions specified in [temp.dep.general]/2. It is not a conversion-function-id, it is not operator=
, and it is not the function name in a dependent call (one that would be subject to ADL). So it's not dependent. That means the name lookup failure for f
should be diagnosed even if A
is not instantiated.