A issue about the second phase name lookup for default argument - c++

#include <iostream>
namespace J {
template <typename T> void zip(int = zap([] { })) { } //#1
template <typename T> int zap(const T &t) {return 0; }
}
int main(){
J::zip<long>();
}
Consider the above code, that's a simplified example of proposed resolution 1664. Note the place marked with #1, I doubt why the name for zap can be looked up in the context of instantiation. I think zap is not a dependent name, the definition of dependent name are the following:
temp.dep
In an expression of the form:
postfix-expression ( expression-list opt)
where the postfix-expression is an unqualified-id, the unqualified-id denotes a dependent name if
any of the expressions in the expression-list is a pack expansion,
any of the expressions or braced-init-lists in the expression-list is type-dependent, or
the unqualified-id is a template-id in which any of the template arguments depends on a template parameter.
I think zap([] { }) does not satisfied any of the above conditions due to the type of expression [] { } is not a dependent-type. Although the following rule says that the namespace associated with closure-type is determined as the following:
temp.inst#11
If a function template f is called in a way that requires a default argument to be used, the dependent names are looked up, the semantics constraints are checked, and the instantiation of any template used in the default argument is done as if the default argument had been an initializer used in a function template specialization with the same scope, the same template parameters and the same access as that of the function template f used at that point, except that the scope in which a closure type is declared ([expr.prim.lambda.closure]) – and therefore its associated namespaces – remain as determined from the context of the definition for the default argument. This analysis is called default argument instantiation. The instantiated default argument is then used as the argument of f.
However, these names both from the context of template definition and the context of instantiation are only considered for the dependent-name, which is ruling by:
temp.dep.res
In resolving dependent names, names from the following sources are considered:
Declarations that are visible at the point of definition of the template.
Declarations from namespaces associated with the types of the function arguments both from the instantiation context ([temp.point]) and from the definition context.
temp.nondep
Non-dependent names used in a template definition are found using the usual name lookup and bound at the point they are used.
So, I think, in order to obey these aforementioned rules, the name lookup for zap only occurs at the point it is used(namely, at #1) due to it's not a dependent-name, that is, the names from the context of instantiation(ADL) are not considered at all.
I test the code in three implementations, the outcomes are listed in the following:
Clang9.0 and higher version views zap as a dependent-name.
The version under 8.0 of Clang reports an error that makes no sense.
The version under 9.1 of gcc views zap as a dependent-name.
The version higher than 9.1 of gcc views zap as a non-dependent name and do not perform the name lookup for zap in the context of instantiation.
So, what is exactly process does the name lookup for zap undergo? It seems the latest version of GCC agrees with considering zap as a non-dependent name which results in that it can't find names for zap. If I miss other rules in the standard, I would appreciate you to point it out.

Your diagnosis is correct, I think, especially given that the example in [temp.nondep] is very similar.
At #1, zap is an unqualified name that is used in a function call expression, within a template definition. It is not a dependent name, so it must be bound at the point of definition. There is no ::J::zap or ::zap in scope (yet), nor could it be found by ADL at this point. This would be different if, for example, the argument type was declared in a namespace that would be overlooked by unqualified name lookup:
namespace I {
struct nondep {};
template <typename T>
int zap(const T&) { return 0; }
}
namespace J {
template <typename T>
void zip(int = zap(I::nondep{})) { } // #1
// 1. Unqualified name lookup of zap finds nothing,
// 2. ADL considers namespace I and finds only I::zap,
// 3. Overload resolution succeeds:
// zap is bound to I::zap<I::nondep>
template <typename T>
int zap(const T&) { return 0; } // Not considered
}
template <typename T>
int zap(const T&) { return 0; } // Not considered
int main() {
J::zip<long>();
}
To illustrate the difference with dependent names, here is a slightly modified example:
namespace J {
struct nondep {};
template <typename T>
int zap(T) { return 2; }
template <typename T>
int zip(int i = zap(T{})) { return i; } // #1, zap is dependent
}
struct nondep {};
template<typename T> int zap(T) { return 1; }
int main() {
// Unqualified name lookup for zap finds J::zap,
// ADL additionally finds ::zap,
// Overload resolution fails:
// this call to zap would be ambiguous
// int x = J::zip<nondep>();
// But here, unqualified lookup and ADL only find J::zap, which is selected
int y = J::zip<J::nondep>(); // y == 2
}
I'm not entirely sure about the scope of the closure type of the lambda, though. According to the wording in [expr.prim.lambda.closure],
The closure type is declared in the smallest block scope, class scope, or namespace scope that contains the corresponding lambda-expression.
It doesn't mention function parameter scope, which would have to place it in namespace J, though regardless of this, I think the compiler should be able to determine that []{} is not dependent on typename T.

Related

Is overload resolution working differently inside template function?

Consider this piece of code.
Try to guess the output of the program before reading further:
#include <iostream>
using namespace std;
template <typename T>
void adl(T) {
cout << "T";
}
struct S {
};
template <typename T>
void call_adl(T t) {
adl(S());
adl(t);
}
void adl(S) {
cout << "S";
}
int main() {
call_adl(S());
}
Are you done? OK.
So the output of this program is TS, which looks counterintuitive (at least for me).
Why is the call adl(S()) inside call_adl using the template overload instead of the one that takes S?
The behaviors of name lookup are different for dependent name and non-dependent name in templates.
For a non-dependent name used in a template definition, unqualified name lookup takes place when the template definition is examined. The binding to the declarations made at that point is not affected by declarations visible at the point of instantiation. For a dependent name used in a template definition, the lookup is postponed until the template arguments are known, at which time ADL examines function declarations with external linkage (until C++11) that are visible from the template definition context as well as in the template instantiation context, while non-ADL lookup only examines function declarations with external linkage (until C++11) that are visible from the template definition context (in other words, adding a new function declaration after template definition does not make it visible except via ADL).
That means for adl(S());, adl(S) declared after call_adl is not visible, then the templated adl(T) is selected. For adl(t);, t is a dependent name (depending on the template parameter T), the lookup is postponed and adl(S) is found by ADL and it wins against adl(T) in overload resolution.

error: no viable overloading with clang, compiles with gcc

The following program compiles fine with g++ (version 10.1.0) but not with clang++ (10.0.0)
#include <iostream>
template <typename U>
struct A { U x; };
namespace tools {
template <typename U>
void operator+=(A<U>& lhs, const A<U>& rhs) { lhs.x += rhs.x; }
}
namespace impl {
template <typename U = int>
void f() {
A<U> a{3};
A<U> b{2};
a += b;
std::cout << a.x << std::endl;
}
}
namespace impl {
using namespace tools;
}
int main()
{
impl::f();
}
The error is:
name.cpp:16:7: error: no viable overloaded '+='
a += b;
~ ^ ~
name.cpp:27:9: note: in instantiation of function template specialization 'impl::f<int>' requested here
impl::f();
Clearly, moving the using namespace tools part before the template function impl::f() removes the error of clang.
Additional note
An important point here is that f is a template function. Without template parameters the code compiles neither with gcc, nor with clang.
What compiler is correct here? gcc or clang?
The code is ill-formed because the part of unqualified name look-up that is not argument dependent is performed in the template definition context. So Clang is right and the GCC bug is already reported (bug #70099)
What followes is the long explanation.
Inside your exemple code there are some place that must be marked, to allow the discussion:
namespace impl {
template <typename U = int>
void f() { // (1) point of definition of the template f
A<U> a{3};
A<U> b{2};
a += b; // call operator += with arguments of dependent type A<U>
std::cout << a.x << std::endl;
}
}
namespace impl {
using namespace tools; // using directive
}
int main()
{
impl::f();
} // (2) point of instantiation of impl::f<int>
At the definition of the template f (1), the call to the operator += is performed with arguments of type A<U>. A<U> is a dependent type, so operator += is a dependent name.
[temp.dep.res]/1 describe how operator += is looked up:
For a function call where the postfix-expression is a dependent name, the candidate functions are found using the usual lookup rules from the template definition context ([basic.lookup.unqual], [basic.lookup.argdep]). [ Note: For the part of the lookup using associated namespaces ([basic.lookup.argdep]), function declarations found in the template instantiation context are found by this lookup, as described in [basic.lookup.argdep]. — end note ][...]
There are two look-ups that are performed.
Non argument dependent unqualified name look up [basic.lookup.unqual].
This look-up is performed from the template definition context. "from the template definition context" means the context at the point of definition of the template. The term "context" refers to the look-up context. If the template f was first declared in namespace impl and then defined in the global namespace scope, unqualified name look-up would still find members of namespace impl. This is why the rule [temp.dep.res]/1 use "the template definition context" and not simply "template definition point".
This look-up is performed from (1) and it does not find the operator += defined in namespace tools. The using directive is appears later than (1), and has no effect.
Argument dependent name look-up (ADL) [basic.lookup.argdep]
ADL is performed at the point of instantiation (2). So it is realized after the using directive. Nevertheless, ADL only considers namespace associated to the type of the arguments. The arguments have type A<int>, the template A is a member of the global namespace, so only members of this namespace can be find by ADL.
At (2) there are no operator += declared in the global namespace scope. So ADL also fails to find a declaration for operator +=.
Seems clang is right here according to this. In short - you are extending your namespace but using namespace should 'propagate' to this extension only forward.
A using-directive specifies that the names in the nominated namespace can be used in the scope in which the using-directive appears after the using-directive.
During unqualified name lookup ([basic.lookup.unqual]), the names appear as if they were declared in the nearest enclosing namespace which contains both the using-directive and the nominated namespace.
[ Note: In this context, “contains” means “contains directly or indirectly”.
— end note
]
Clang is right: unqualified dependent name lookup considers only declarations that are visible at the point of definition of the template
In your example, the operator+= is a dependent name in the function template f, in which case unqualified name lookup for the a += b; call considers only declarations that are visible at the point of definition of the function template f. As the tools namespace is added as a nominated namespace to impl only after the point of definition of f, unqual. name lookup will not see the declarations brought in from tools, and will fail to tools::operator+=. Thus, Clang is right here, whereas GCC as well as MSVC, is wrong in not rejecting the code.
This behaviour for GCC seems to only be present when the dependent name refers to an operator function, whereas if we replace the operator with a named function, GCC also rejects the code.
Rejected by Clang, accepted by GCC:
struct Dummy{};
namespace ns_g {
template <typename T>
bool operator!(T) { return true; }
} // namespace ns_f
namespace ns_f {
template <typename T>
void f() {
(void)(!T{});
}
// Add ns_g as a nominated namespace to ns_f
// _after_ point of definition of ns_f::f.
using namespace ns_g;
} // namespace ns_f
int main() {
ns_f::f<Dummy>();
return 0;
}
Rejected by both Clang and by GCC:
struct Dummy{};
namespace ns_g {
template <typename T>
bool g(T) { return true; }
} // namespace ns_f
namespace ns_f {
template <typename T>
void f() {
(void)(g(T{}));
}
// Add ns_g as a nominated namespace to ns_f
// _after_ point of definition of ns_f::f.
using namespace ns_g;
} // namespace ns_f
int main() {
ns_f::f<Dummy>();
return 0;
}
where, for the latter, GCC even gives us a note that:
note: 'template<class T> bool ns_g::g(T)' declared
here, later in the translation unit.
This inconsistency alone hints that GCC is wrong in the former example, and we may not that Clang's language compatibility page explicitly mentions that some versions of GCC may accept invalid code:
Language Compatibility
[...]
Unqualified lookup in templates
Some versions of GCC accept the following invalid code: [...]
Even if the particular example pointed out by Clang is rejected also by more recent GCC versions, the context of this questions is the same.
Open bug report on GCC
Note that the OP (and answerer) to a similar SO question (which I found long after all the answers landed on this question), to which this question is probably a duplicate:
Dependent name lookup in function template: clang rejects, gcc accepts
submitted a bug report on GCC that is yet to be claimed/addressed:
Bug 70099 - Function found by ADL, but shouldn't be visible at point of definition
(All ISO Standard references below refer to N4659: March 2017 post-Kona working draft/C++17 DIS)
Standard references
Even if [temp.res]/9 states [extract, emphasis mine]:
[temp.res]/9 When looking for the declaration of a name used in a template
definition, the usual lookup rules ([basic.lookup.unqual],
[basic.lookup.argdep]) are used for non-dependent names. The lookup
of names dependent on the template parameters is postponed until the
actual template argument is known ([temp.dep]). [ Example: ... ]
[...]
[temp.dep.res]/1 is clear that only declarations that are visible at the point of definition of the template are considered for non-qualified (dependent) name lookup [emphasis mine]:
[temp.dep.res]/1 In resolving dependent names, names from the following sources are
considered:
(1.1) Declarations that are visible at the point of definition of the template.
(1.2) Declarations from namespaces associated with the types of the function arguments both from the instantiation context ([temp.point])
and from the definition context.
a fact that is repeated in [temp.dep.candidate]/1 [emphasis mine]:
[temp.dep.candidate]/1 For a function call where the postfix-expression is a dependent name,
the candidate functions are found using the usual lookup rules
([basic.lookup.unqual], [basic.lookup.argdep]) except that:
(1.1) For the part of the lookup using unqualified name lookup, only function declarations from the template definition context are found.
(1.2) For the part of the lookup using associated namespaces ([basic.lookup.argdep]), only function declarations found in either
the template definition context or the template instantiation context
are found.
where the wording template definition context is used instead of point of definition of the template, but afaik these are equivalent.
As per [namespace.udir]/2 [emphasis mine]:
A using-directive specifies that the names in the nominated namespace can be used in the scope in which the using-directive appears after the using-directive. During unqualified name lookup, the names appear as if they were declared in the nearest enclosing namespace which contains both the using-directive and the nominated namespace. [ Note: In this context, “contains” means “contains directly or indirectly”.  — end note ]
placing the using directive after the point of definition of the function template f is equivalent to simply declaring a name after the same point of definition, and as expected, the following modified example is rejected by Clang but accepted by GCC:
struct Dummy{};
namespace ns_f {
template <typename T>
void f() {
(void)(!T{});
}
template <typename T>
bool operator!(T) { return true; }
} // namespace ns_f
int main() {
ns_f::f<Dummy>();
return 0;
}
Finally, note that ADL, (1.2) in [temp.dep.candidate]/1 above, does not apply here, as ADL do not proceed to enclosing scopes.
Non-dependent constructs may be diagnosed without instantiations
Additional note An important point here is that f is a template function. Without template parameters the code would not compile neither with gcc, nor with clang.
If A were to be made into a non-template class, say
struct A { int x; };
then [temp.res]/8.3 applies, and the program is ill-formed, no diagnostic required:
[temp.res]/8 Knowing which names are type names allows the syntax of every template to be checked. The program is ill-formed, no diagnostic required, if:
[...]
(8.3) a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, or
[...]

Question about resolution of free function names in C++ templates

This program works as expected:
#include <iostream>
template <typename T>
void output(T t) {
prt(t);
}
struct It {
It(int* p) : p(p) {}
int* p;
};
void prt(It it) {
std::cout << *(it.p) << std::endl;
}
int main() {
int val = 12;
It it(&val);
output(it);
return 0;
}
When you compile and execute this, it prints "12" as it should. Even though the function prt, required by the output template function, is defined after output, prt is visible at the point of instantiation, and therefore everything works.
The program below is very similar to the program above, but it fails to compile:
#include <iostream>
template <typename T>
void output(T t) {
prt(t);
}
void prt(int* p) {
std::cout << (*p) << std::endl;
}
int main() {
int val = 12;
output(&val);
return 0;
}
This code is trying to do the same thing as the previous example, but this fails in gcc 8.2 with the error message:
'prt' was not declared in this scope, and no declarations were found by
argument-dependent lookup at the point of instantiation [-fpermissive]
The only thing that changed is that the argument passed to output is a built-in type, rather than a user-defined type. But I didn't think that should matter for name resolution. So my question is: 1) why does the second example fail?; and 2) why does one example fail and the other succeeds?
The Standard rule that applies here is found in [temp.dep.candidate]:
For a function call where the postfix-expression is a dependent name, the candidate functions are found using the usual lookup rules ([basic.lookup.unqual], [basic.lookup.argdep]) except that:
For the part of the lookup using unqualified name lookup, only function declarations from the template definition context are found.
For the part of the lookup using associated namespaces ([basic.lookup.argdep]), only function declarations found in either the template definition context or the template instantiation context are found.
In both examples, unqualified name lookup finds no declarations of prt, since there were no such declarations before the point where the template was defined. So we move on to argument-dependent lookup, which looks only in the associated namespaces of the argument types.
Class It is a member of the global namespace, so the global namespace is the one associated namespace, and the one declaration is visible within that namespace in the template instantiation context.
A pointer type U* has the same associated namespaces as type U, and a fundamental type has no associated namespaces at all. So since the only argument type int* is a pointer to fundamental type, there are no associated namespaces, and argument-dependent lookup can't possibly find any declarations in the second program.
I can't exactly say why the rules were designed this way, but I would guess the intent is that a template should either use the specific declared functions it meant to use, or else use a function as an extensible customization point, but those user customizations need to be closely related to a user-defined type they will work with. Otherwise, it becomes possible to change the behavior of a template that really meant to use one specific function or function template declaration by providing a better overload for some particular case. Admittedly, this is more from the viewpoint of when there is at least one declaration in the template definition context, not when that lookup finds nothing at all, but then we get into cases where SFINAE was counting on not finding something, etc.

Can't understand name lookup differences between an int and a user defined type - perhaps ADL related

Why does the following code compile:
template<typename T>
void foo(T in) { bar(in); }
struct type{};
void bar(type) {}
int main() { foo(type()); }
When the following does not:
template<typename T>
void foo(T in) { bar(in); }
void bar(int) {}
int main() { foo(42); }
Compiling with GnuC++ 7:
a.cpp: In instantiation of 'void foo(T) [with T = int]':
a.cpp:9:20: required from here
a.cpp:2:21: error: 'bar' was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
void foo(T in) { bar(in); }
~~~^~~~
a.cpp:8:6: note: 'void bar(int)' declared here, later in the translation unit void bar(int) {}
I would assume that MSVC would compile both (as it does) but that GCC would reject both since GCC/Clang have proper two phase name lookup...
The strange part is not that the int example fails to compile, it is that the type example does since bar is defined after foo. This is due to [temp.dep.candidate] (see third paragraph).
Two-pass compilation of templates
When the compiler parses and compiles a template class or function, it looks up identifiers in two pass:
Template argument independent name lookup: everything that does not depend on the template arguments can be checked. Here, since bar() depends on a template argument, nothing is done. This lookup is done at the point of definition.
Template argument dependent name lookup: everything that could not be looked up in pass #1 is now possible. This lookup is done at the point of instantiation.
You get an error during pass #2.
ADL lookup
When a function name is looked up, it is done within the current context and those of the parameters type. For instance, the following code is valid though f is defined in namespace n:
namespace n { struct type {}; void f(type) {}; }
int main() { n::type t; f(t); } // f is found in ::n because type of t is in ::n
More about ADL (cppreference.com):
Argument-dependent lookup, also known as ADL, or Koenig lookup, is the set of rules for looking up the unqualified function names in function-call expressions, including implicit function calls to overloaded operators. These function names are looked up in the namespaces of their arguments in addition to the scopes and namespaces considered by the usual unqualified name lookup.
Two-pass compilation, ADL lookup and unqualified-id lookup
In your case, those three mechanisms collide. See [temp.dep.candidate]:
For a function call that depends on a template parameter, if the function name is an unqualified-id but not a template-id, the
candidate functions are found using the usual lookup rules (3.4.1,
3.4.2) except that:
— For the part of the lookup using unqualified name lookup (3.4.1), only function declarations with external linkage from the
template definition context are found.
— For the part of the lookup using associated namespaces (3.4.2), only function declarations with external linkage found in either the
template definition context or the template instantiation context are
found.
So, with foo(type()) unqualified-id lookup kicks in and the lookup is done "in either the template definition context or the template instantiation".
With foo(42), 42 being a fundamental type, ADL is not considered and only the "definition context" is considered.
The 1st sample is valid, because ADL takes effect for the name lookup of dependent name in template definition; which makes it possible to find the function bar. (bar(in) depends on the template parameter T.)
(emphasis mine)
For a dependent name used in a template definition, the lookup is postponed until the template arguments are known, at which time ADL examines function declarations that are visible from the template definition context as well as in the template instantiation context, while non-ADL lookup only examines function declarations that are visible from the template definition context (in other words, adding a new function declaration after template definition does not make it visible except via ADL).
And ADL doesn't work with fundamental types, that's why the 2nd sample fails.

Declare function after template defined

Let's say I have a template function:
template <class T>
void tfoo( T t )
{
foo( t );
}
later I want to use it with a type, so I declare/define a function and try to call it:
void foo( int );
int main()
{
tfoo(1);
}
and I am getting error from g++:
‘foo’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
foo( t );
why it cannot find void foo(int) at the point of instantiation? It is declared at that point. Is there a way to make it work (without moving declaration of foo before template)?
foo in your case is a dependent name, since function choice depends on the type if the argument and the argument type depends on the template parameter. This means that foo is looked up in accordance with the rules of dependent lookup.
The difference between dependent and non-dependent lookup is that in case of dependent lookup ADL-nominated namespaces are seen as extended: they are extended with extra names visible from the point of template instantiation (tfoo call in your case). That includes the names, which appeared after the template declaration. The key point here is that only ADL-nominated namespaces are extended in this way.
(By ADL-nominated namespace I refer to namespace associated with function argument type and therefore brought into consideration by the rules of dependent name lookup. See "3.4.2 Argument-dependent name lookup")
In your case the argument has type int. int is a fundamental type. Fundamental types do not have associated namespaces (see "3.4.2 Argument-dependent name lookup"), which means that it does not nominate any namespace through ADL. In your example ADL is not involved at all. Dependent name lookup for foo in this case is no different from non-dependent lookup. It will not be able to see your foo, since it is declared below the template.
Note the difference with the following example
template <class T> void tfoo( T t )
{
foo( t );
}
struct S {};
void foo(S s) {}
int main()
{
S s;
tfoo(s);
}
This code will compile since argument type S is a class type. It has an associated namespace - the global one - and it adds (nominates) that global namespace for dependent name lookup. Such ADL-nominated namespaces are seen by dependent lookup in their updated form (as seen from the point of the call). This is why the lookup can see foo and completes successfully.
It is a rather widespread misconception when people believe that the second phase of so called "two-phase lookup" should be able to see everything that was additionally declared below template definition all the way to the point of instantiation (point of the call in this case).
No, the second phase does not see everything. It can see the extra stuff only in namespaces associated with function arguments. All other namespaces do not get updated. They are seen as if observed from the point of template definition.