Home > database >  first friend function template declaration is visible through `using namespace` but not through qual
first friend function template declaration is visible through `using namespace` but not through qual

Time:01-20

I have the following code I compile with C 11. This code is a simplification of a real world usage.

namespace bar {
template<int M = 10>
struct S {
    template<int N = M*2>
    friend int foo(S) { 
        return N-M; 
    }
};

template<int M, int N>
int foo(S<M>);

} /* end of bar */

int main() {
    bar::S<> s;
    /* OK    { using namespace bar; return foo<42>(s)   foo<>(s); } // */
    /* NOK   { return foo<42>(s)   foo<>(s); } // */
    /* NOK   { using namespace bar; return bar::foo<42>(s)   bar::foo<>(s); } // */
    /* NOK */{ return bar::foo<42>(s)   bar::foo<>(s); } // */
}

My question is: Why does it compile with using namespace bar; with unqualified name lookup (see line with /* OK )? While it does not compile in the variant using qualified name lookup (bar::foo<>()) or unqualified name lookup without using namespace (see lines with /* NOK )?

CodePudding user response:

The namespace bar contains declarations for different template functions called foo.

One is template<int M, int N> int foo(S<M>); declared at the end, and there is one for each S<M> declared as friend functions.

A friend function can only be found via ADL. So when you have bar::foo<42>(s), you could only possibly be calling the free function (Where 42 is the value of M and N can't be deduced, so it doesn't compile).

Prior to C 20, foo<42>(s) would be interpreted as foo < 42 > (s) ((foo < 42) > s with equivalent bracketing), since the < is not interpreted as a template head because lookup for foo doesn't find a template.

When you add using namespace bar;, lookup for foo finds the template function, so foo<42>(s) gets parsed as a templated function call. This eventually picks the friend function found via ADL during overload resolution.

This is fixed by adding a function template foo in the global namespace:

// Only for ADL purposes so `foo` is parsed as a template
template<typename> void foo() = delete;

int main() {
    bar::S<> s;
    return foo<42>(s)   foo<>(s);
}
  • Related