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);
}