This is from the standard (C 20) - unqualified name lookup 6.5.2.
Can anyone please explain what's going on here ? Note: this is not ADL. I am specifically looking for an elucidation of this brief sentence: "In some cases a name followed by <
is treated as a template-name even though name lookup did not find a template-name"
int h;
void g();
namespace N{
struct A{};
template <class T> int f(T);
template <class T> int g(T);
template <class T> int h(T);
}
int x = f<N::A>(N::A()); // OK: lookup of f finds nothing; f treated as template name
int y = g<N::A>(N::A()); // OK: lookup of g finds a function, g treated as template name
int z = h<N::A>(N::A()); // error: h< does not begin a template-id
CodePudding user response:
It is something called argument-dependent name lookup [6.5.2], the special rules when unqualified name lookup is used.
When calling a function [or function template, rules are the same], ADL makes the compiler consider the namespace of its arguments.
For function [template] f()
compiler sees, that you provided argument in the namespace N
, so it [compiler] considers name lookup in that scope too.
With function [template] g()
it is almost the same, except the void g();
declaration is considered at the overload resolution, since the ::g()
[top-level namespace] does not have any arguments, N::g()
wins.
With h()
it is different. Variable names cannot be overloaded. For the top-level namespace h
is a variable, so you can't use it as a function or function template.
CodePudding user response:
In f<N::A>(N::A())
, the <
can either be a less-than operator or start a template argument list. To disambiguate, compilers need to see whether f
(the name before <
) names a template.
Note that it's impossible to perform ADL at this point, because when compilers see <
, they do not even know whether there are arguments.
C 20 specifies that if usual name lookup finds nothing, f
is treated as a template-name. Thus
<
in question is considered to start a template argument list, which meansf<N::A>
is parsed as a template-id, which meansf<N::A>(N::A())
is parsed as a function call expression, which means- ADL is performed to see what
f
really is.
Before C 20, the name before <
is treated as a template-name if and only if usual name lookup finds a template, so none of these happen, <
is considered as an operator, and f<N::A>(N::A())
is treated as an error.
The change was made by P0846R0.
C 20 [temp.name]/2:
An identifier is a template-name if it is associated by name lookup with a template or an overload set that contains a function template, or the identifier is followed by
<
, the template-id would form an unqualified-id, and name lookup either finds one or more functions or finds nothing.
C 20 ([temp.names]/3):
When a name is considered to be a template-name, and it is followed by a
<
, the<
is always taken as the delimiter of a template-argument-list and never as the less-than operator.
The latest draft arguably makes it clearer, by specifying that ([temp.names]/3):
A
<
is interpreted as the delimiter of a template-argument-list if it follows a name that is not a conversion-function-id and
- [...]
- that is an unqualified name for which name lookup either finds one or more functions or finds nothing, or
- [...].
CodePudding user response:
The compiler starts the lookup in the current namespace. It then, if needed, goes on to look in the namespace(s) of parameters to find possible function overloads.
In the last case h
is found as int h
, not a function. So no need to look for function overloads. The compiler then finds that using h
as a template function doesn't work (as it is int h;
- an error).