Consider the following fragment:
struct X { };
namespace foo {
template <class T>
void bar() { T{} < T{}; }
void operator<(const X&, const X&) {}
}
int main() {
foo::bar<X>();
}
clang rejects this code, gcc accepts it. Is this a gcc bug or is this a clang bug?
I believe this is a gcc bug, filed as 70099. From [temp.dep.res]:
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 (14.6.4.1) and from the definition context.
foo::operator<() isn't visible at the point of definition of the template, and isn't in an associated namespace from the function arguments (X's associated namespace is just the global namespace ::). So I think gcc is wrong to find foo::operator< and clang is correct to reject the code.
GCC is wrong Clang is correct. The fact that GCC swallows invalid code as the one that you showed is also mentioned in CLANG's compatibility page here.
Unqualified names are looked up in the following ways.
The compiler conducts unqualified lookup in the scope where the name was written. For a template, this means the lookup is done at the point where the template is defined, not where it's instantiated. Since operator< hasn't been declared yet at this point, unqualified lookup won't find it.
If the name is called like a function, then the compiler also does argument-dependent lookup (ADL). (Sometimes unqualified lookup can suppress ADL; see [basic.lookup.argdep] paragraph 3 for more information.) In ADL, the compiler looks at the types of all the arguments to the call. When it finds a class type, it looks up the name in that class's namespace; the result is all the declarations it finds in those namespaces, plus the declarations from unqualified lookup. However, the compiler doesn't do ADL until it knows all the argument types.
Related
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
[...]
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.
Consider the code below:
#include <utility>
void f(int, int);
void g(int, int);
struct functor
{
template<typename... T>
void operator()(T&&... params)
{
return f(std::forward<T>(params)...);
}
};
int main()
{
functor()(1); // can use the default value here, why?!
// g(1); // error here as expected, too few arguments
}
void f(int a, int b = 42) {}
void g(int a, int b = 24) {}
This is a thin wrapper around a function call. However, inside functor::operator(), f doesn't have its default value for the second parameter known (it is visible only after main, in the definition), so the code should not compile. g++5.2 compiles it successfully though, but clang++ spits out the expected message that one expects for compilers that perform the two-phase name lookup correctly:
error: call to function 'f' that is neither visible in the
template definition nor found by argument-dependent lookup
return f(std::forward(params)...);
Is this a gcc bug or I am missing something here? I.e., is the point of instantiation after the definition of f below main()? But even in this case, it shouldn't work, as at the second phase the function can only be found via ADL, which is not the case here.
[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 ([basic.lookup.unqual]), 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.
If the call would be ill-formed or would find a better match had the lookup within the associated namespaces
considered all the function declarations with external linkage introduced in those namespaces in all translation units, not just considering those declarations found in the template definition and template instantiation
contexts, then the program has undefined behavior.
Note that ADL is not even working here, as the involved types are fundamental (their set of associated namespaces is empty).
Here is simple code presented which should have worked according to c++ standard I believe :
template<typename T>
void foo(T x)
{
bar(x);
void bar(int);
}
void bar(int) { }
int main()
{
foo(0);
}
Error comes as from GCC 4.7 as:
‘bar’ was not declared in this scope, and no declarations were found
by argument-dependent lookup at the point of instantiation
But in the C++ standard it's written. § 14.6.4.2 :
For a function call that depends on a template parameter, the
candidate functions are found using the usual lookup rules (3.4.1,
3.4.2, 3.4.3) except that:
— For the part of the lookup using unqualified name lookup (3.4.1) or qualified name lookup (3.4.3), only function declarations from the template definition context are found.
I may be have got the wrong impression of what's written, can anyone please correct me here?
You should just move the declaration of 'bar' to the top. Because at the point where the template is defined (not instantiated), before 'bar' is invoked, it hasn't be declared.
I'm wondering why the following code compiles.
#include <iostream>
template<class T>
void print(T t) {
std::cout << t;
}
namespace ns {
struct A {};
}
std::ostream& operator<<(std::ostream& out, ns::A) {
return out << "hi!";
}
int main() {
print(ns::A{});
}
I was under impression that at the instantiation point unqualified dependent names are looked-up via ADL only - which should not consider the global namespace. Am I wrong?
This is an interesting case. The workings of name lookup as you describe them is summarized here:
[temp.dep.candidate] (emphasis mine)
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:
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.
If the call would be ill-formed or would find a better match had the
lookup within the associated namespaces considered all the function
declarations with external linkage introduced in those namespaces in
all translation units, not just considering those declarations found
in the template definition and template instantiation contexts, then
the program has undefined behavior.
The bit I highlighted is the crux of the matter. The description for "ADL only" is for function calls of the from foo(bar)! It does not mention calls that result from an overloaded operator. We know that calling overloaded operators is equivalent to calling a function, but the paragraph speaks of expressions in a specific form, that of a function call only.
If one was to change your function template into
template<class T>
void print(T t) {
return operator<< (std::cout, t);
}
where now a function is called via postfix-expression notation, then wo and behold: GCC emits an equivalent error to Clang. It implements the above paragraph reliably, just not when it comes to overloaded operator calls.
So is it a bug? I would say it is. The intent is surely that overloaded operators be found like named functions (even when called from their respective expression form). So GCC needs to be fixed. But the standard could use a minor clarification of the wording too.