Search code examples
c++templateslanguage-lawyerfriend

A template friend function inside a template class


The question of implementing a template friend function inside a template class was already discussed in the past and seem to be an unresolved issue in the standard with different behavior of the different compilers.

Checking newest available versions of gcc and clang it seems that the decision is taken, to require the implementation of template friend function to be outside of the template class.

The following code is rejected by newest versions of both gcc and clang (gcc x86-64 9.2.0 and clang x86-64 9.0.0) when compiled with -std=c++2a. Past versions of clang are ok with it (e.g. clang x86-64 7.0.0).

template<long Num>
struct A {
    template<long Num1, long Num2>
    friend int foo(A<Num1> a1, A<Num2> a2) {
        return 1;
    }
    // the compilation error occurs only with a template friend function
    // and only if *implemented inside* a template class
};

int main() {
    A<1> a1;
    A<2> a2; // commenting this line removes the error
}

Both compilers complain on redefinition of foo:

<source>:4:16: error: redefinition of 'foo'    
    friend int foo(A<Num1> a1, A<Num2> a2) {    
               ^    
<source>:11:10: note: in instantiation of template class 'A<2>' requested here    
    A<2> a2;    
         ^

Is there a new official resolution on the subject or is it just the latest compilers fashion?

https://godbolt.org/z/ySrVe3


Solution

  • I figured out the relevant wording from the resolution of CWG 2174. It is in [temp.inst]/2 in C++17:

    However, for the purpose of determining whether an instantiated redeclaration is valid according to [basic.def.odr] and [class.mem], a declaration that corresponds to a definition in the template is considered to be a definition.

    Thus, the compiler is required to defer the actual instantiation of the definition until the point where a definition of foo is required to exist (in your code, there is no such point, so foo is not instantiated), but even if the definition is not instantiated, the compiler is still required to diagnose multiple definitions within the same translation unit as if the definition had been instantiated every time the declaration were instantiated.

    It seems to me that this rule makes it unnecessarily difficult to use friend functions for no obvious benefit, and that it would be better if multiple definitions of a friend function defined inside a class template, instantiated within the same TU, were considered as a single definition. Perhaps, if I had more time on my hands, I would propose making such a change to the standard. (Yes, I am saying I don't have the time, so if anyone else wants to do this, please go ahead without worrying about duplicated effort.)

    Unless such a change is made, it seems that you do, in fact, need to define the friend function outside the class template.