error: no viable overloading with clang, compiles with gcc - c++

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
[...]

Related

A issue about the second phase name lookup for default argument

#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.

Why doesn't function declared inside other function participate in argument dependent lookup?

Consider a simple example:
template <class T>
struct tag { };
int main() {
auto foo = [](auto x) -> decltype(bar(x)) { return {}; };
tag<int> bar(tag<int>);
bar(tag<int>{}); // <- compiles OK
foo(tag<int>{}); // 'bar' was not declared in this scope ?!
}
tag<int> bar(tag<int>) { return {}; }
Both [gcc] and [clang] refuses to compile the code. Is this code ill-formed in some way?
foo(tag<int>{}); triggers the implicit instantiation of a specialization of the function call operator member function template of foo's closure type with the template argument tag<int>. This creates a point of instantiation for this member function template specialization. According to [temp.point]/1:
For a function template specialization, a member function template
specialization, or a specialization for a member function or static
data member of a class template, if the specialization is implicitly
instantiated because it is referenced from within another template
specialization and the context from which it is referenced depends on
a template parameter, the point of instantiation of the specialization
is the point of instantiation of the enclosing specialization.
Otherwise, the point of instantiation for such a specialization
immediately follows the namespace scope declaration or definition
that refers to the specialization.
(emphasis mine)
So, the point of instantiation is immediately after main's definition, before the namespace-scope definition of bar.
Name lookup for bar used in decltype(bar(x)) proceeds according to [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
(6.4.1, 6.4.2) except that:
(1.1) — For the part of the lookup using unqualified name lookup
(6.4.1), only function declarations from the template definition
context are found.
(1.2) — For the part of the lookup using associated namespaces
(6.4.2), only function declarations found in either the template
definition context or the template instantiation context are found. [...]
Plain unqualified lookup in the definition context doesn't find anything. ADL in the definition context doesn't find anything either. ADL in the instantiation context, according to [temp.point]/7:
The instantiation context of an expression that depends on the
template arguments is the set of declarations with external linkage
declared prior to the point of instantiation of the template
specialization in the same translation unit.
Again, nothing, because bar hasn't been declared at namespace scope yet.
So, the compilers are correct. Moreover, note [temp.point]/8:
A specialization for a function template, a member function template,
or of a member function or static data member of a class template may
have multiple points of instantiations within a translation unit, and
in addition to the points of instantiation described above, for any
such specialization that has a point of instantiation within the
translation unit, the end of the translation unit is also considered
a point of instantiation. A specialization for a class template has
at most one point of instantiation within a translation unit. A
specialization for any template may have points of instantiation in
multiple translation units.
If two different points of instantiation give a template specialization different meanings according to the one-definition rule
(6.2), the program is ill-formed, no diagnostic required.
(emphasis mine)
and the second part of [temp.dep.candidate]/1:
[...] 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.
So, ill-formed NDR or undefined behavior, take your pick.
Let's consider the example from your comment above:
template <class T>
struct tag { };
auto build() {
auto foo = [](auto x) -> decltype(bar(x)) { return {}; };
return foo;
}
tag<int> bar(tag<int>) { return {}; }
int main() {
auto foo = build();
foo(tag<int>{});
}
Lookup in the definition context still doesn't find anything, but the instantiation context is immediately after main's definition, so ADL in that context finds bar in the global namespace (associated with tag<int>) and the code compiles.
Let's also consider AndyG's example from his comment above:
template <class T>
struct tag { };
//namespace{
//tag<int> bar(tag<int>) { return {}; }
//}
auto build() {
auto foo = [](auto x) -> decltype(bar(x)) { return {}; };
return foo;
}
namespace{
tag<int> bar(tag<int>) { return {}; }
}
int main() {
auto foo = build();
foo(tag<int>{});
}
Again, the instantiation point is immediately after main's definition, so why isn't bar visible? An unnamed namespace definition introduces a using-directive for that namespace in its enclosing namespace (the global namespace in this case). This would make bar visible to plain unqualified lookup, but not to ADL according to [basic.lookup.argdep]/4:
When considering an associated namespace, the lookup is the same as
the lookup performed when the associated namespace is used as a
qualifier (6.4.3.2) except that:
(4.1) — Any using-directives in the associated namespace are
ignored. [...]
Since only the ADL part of the lookup is performed in the instantiation context, bar in the unnamed namespace is not visible.
Commenting out the lower definition and uncommenting the upper one makes bar in the unnamed namespace visible to plain unqualified lookup in the definition context, so the code compiles.
Let's also consider the example from your other comment above:
template <class T>
struct tag { };
int main() {
void bar(int);
auto foo = [](auto x) -> decltype(bar(decltype(x){})) { return {}; };
tag<int> bar(tag<int>);
bar(tag<int>{});
foo(tag<int>{});
}
tag<int> bar(tag<int>) { return {}; }
This is accepted by GCC, but rejected by Clang. While I was initially quite sure that this is a bug in GCC, the answer may actually not be so clear-cut.
The block-scope declaration void bar(int); disables ADL according to [basic.lookup.argdep]/3:
Let X be the lookup set produced by unqualified lookup (6.4.1) and let
Y be the lookup set produced by argument dependent lookup (defined as
follows). If X contains
(3.1) — a declaration of a class member, or
(3.2) — a block-scope function declaration that is not a
using-declaration, or
(3.3) — a declaration that is neither a function nor a function
template
then Y is empty. [...]
(emphasis mine)
Now, the question is whether this disables ADL in both the definition and instantiation contexts, or only in the definition context.
If we consider ADL disabled in both contexts, then:
The block-scope declaration, visible to plain unqualified lookup in the definition context, is the only one visible for all instantiations of the closure type's member function template specializations. Clang's error message, that there's no viable conversion to int, is correct and required - the two quotes above regarding ill-formed NDR and undefined behavior don't apply, since the instantiation context doesn't influence the result of name lookup in this case.
Even if we move bar's namespace-scope definition above main, the code still doesn't compile, for the same reason as above: plain unqualified lookup stops when it finds the block-scope declaration void bar(int); and ADL is not performed.
If we consider ADL disabled only in the definition context, then:
As far as the instantiation context is concerned, we're back to the first example; ADL still can't find the namespace-scope definition of bar. The two quotes above (ill-formed NDR and UB) do apply however, and so we can't blame a compiler for not issuing an error message.
Moving bar's namespace-scope definition above main makes the code well-formed.
This would also mean that ADL in the instantiation context is always performed for dependent names, unless we have somehow determined that the expression is not a function call (which usually involves the definition context...).
Looking at how [temp.dep.candidate]/1 is worded, it seems to say that plain unqualified lookup is performed only in the definition context as a first step, and then ADL is performed according to the rules in [basic.lookup.argdep] in both contexts as a second step. This would imply that the result of plain unqualified lookup influences this second step as a whole, which makes me lean towards the first option.
Also, an even stronger argument in favor of the first option is that performing ADL in the instantiation context when either [basic.lookup.argdep]/3.1 or 3.3 apply in the definition context doesn't seem to make sense.
Still... it may be worth asking about this one on std-discussion.
All quotes are from N4713, the current standard draft.
From unqualified lookup rules ([basic.lookup.unqual]):
For the members of a class X, a name used in a member function body, [...], shall be declared in one of the
following ways
— if X is a local class or is a nested class of a local class, before the definition of class X in a block
enclosing the definition of class X
Your generic lambda is a local class within main, so to use bar, the name bar must appear in a declaration beforehand.

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.

Namespace causes sub-optimal template overload resolution

This is very similar to this question, but I'm not sure the answer there is entirely applicable to the minimal code I've put together that demonstrates the issue. (My code does not use trailing-return types, and there are some other differences as well.) Additionally, the issue of whether MSVC's behavior is legal doesn't seem to be addressed.
In short, I'm seeing the compiler select a generic function template instantiation rather than a more-specific overload when the function template is inside a namespace.
Consider the following set of namespace and class definitions:
namespace DoStuffUtilNamespace
{
template<typename UNKNOWN>
void doStuff(UNKNOWN& foo)
{
static_assert(sizeof(UNKNOWN) == -1, "CANNOT USE DEFAULT INSTANTIATION!");
}
}
class UtilForDoingStuff
{
public:
template <typename UNKNOWN>
void doStuffWithObjectRef(UNKNOWN& ref)
{
DoStuffUtilNamespace::doStuff(ref);
}
};
class MyClassThatCanDoStuff { };
namespace DoStuffUtilNamespace
{
using ::MyClassThatCanDoStuff; // No effect.
void doStuff(MyClassThatCanDoStuff& foo) { /* No assertion! */ }
}
... and the following use-cases:
int main()
{
MyClassThatCanDoStuff foo;
DoStuffUtilNamespace::MyClassThatCanDoStuff scoped_foo;
UtilForDoingStuff util;
DoStuffUtilNamespace::doStuff(foo); // Compiles
DoStuffUtilNamespace::doStuff(scoped_foo); // Compiles
util.doStuffWithObjectRef(foo); // Triggers static assert
util.doStuffWithObjectRef(scoped_foo); // Triggers static assert
}
If the entire DoStuffUtilNamespace is eliminated and all its members are moved to global scope, this compiles fine with G++ and Clang++.
With the namespace, doStuff is of course a dependent name. According to the top-voted answer on the similar question, the standard says:
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 and from the definition context.
This seems a little odd to me; I don't understand why the first bullet point would specify that the declarations must be visible at the point of definition of the template rather than at the point of instantiation, since the second bullet point explicitly specifies that some declarations visible only at the point of instantiation are allowed. (If someone would like to offer a rationale, I'd appreciate it, but that's not my question because it's my understanding that questions of the form "why did the standards committee decide X" are off topic.)
So I think that explains why util.doStuffWithObjectRef(foo); triggers the static assertion: doStuff(MyClassThatCanDoStuff&) hasn't been declared at the point of definition of UtilForDoingStuff::doStuffWithObjectRef<UNKNOWN>(UNKNOWN&). And indeed moving the class UtilForDoingStuff definition after the doStuff overload has been defined seems to fix the issue.
But what exactly does the standard mean by "namespaces associated with the types of the function arguments"? Shouldn't the using ::MyClassThatCanDoStuff declaration, together with the explicit scoping of the scoped_foo instance type within the namespace, trigger argument-dependent lookup, and shouldn't this look-up find the non-asserting definition of doStuff()?
Also, the entire code is compiled without error using clang++ -ftemplate-delayed-parsing, which emulates MSVC's template-parsing behavior. This seems preferable, at least in this particular case, because the ability to add new declarations to a namespace at any time is one of the primary appeals of namespaces. But, as noted above, it doesn't quite seem to follow the letter of the law, according to the standard. Is it permissible, or is it an instance of non-conformance?
EDIT:: As pointed out by KIIV, there is a workaround; the code compiles if template specialization is used instead of overloading. I would still like to know the answers to my questions about the standard.
With the namespace, doStuff is of course a dependent name.
You are starting from the wrong premise. There is no ADL for a qualified call like DoStuffUtilNamespace::doStuff(ref). [basic.lookup.argdep]/p1, emphasis mine:
When the postfix-expression in a function call (5.2.2) is an
unqualified-id, other namespaces not considered during the usual
unqualified lookup (3.4.1) may be searched, and in those namespaces,
namespace-scope friend function or function template declarations
(11.3) not otherwise visible may be found.
DoStuffUtilNamespace::doStuff is a qualified-id, not an unqualified-id. ADL doesn't apply.
For this reason, DoStuffUtilNamespace::doStuff is also not a dependent name. [temp.dep]/p1:
In an expression of the form:
postfix-expression ( expression-listopt)
where the postfix-expression is an unqualified-id, the
unqualified-id denotes a dependent name if [...]. If an operand of an operator is a type-dependent expression, the operator also denotes
a dependent name. Such names are unbound and are looked up at the
point of the template instantiation (14.6.4.1) in both the context of
the template definition and the context of the point of instantiation
(The italicization of dependent name indicate that this paragraph is defining the term.)
Instead, per [temp.nondep]/p1:
Non-dependent names used in a template definition are found using the
usual name lookup and bound at the point they are used.
which doesn't find your later overload declaration.
Specialization works because it's still the same function template declaration that's used; you just supplied a different implementation than the default one.
But what exactly does the standard mean by "namespaces associated with
the types of the function arguments"? Shouldn't the using ::MyClassThatCanDoStuff declaration, together
with the explicit scoping of the scoped_foo instance type within the
namespace, trigger argument-dependent lookup
No. using-declarations do not affect ADL. [basic.lookup.argdep]/p2, emphasis mine:
For each argument type T in the function call, there is a set of
zero or more associated namespaces and a set of zero or more
associated classes to be considered. The sets of namespaces and
classes is determined entirely by the types of the function arguments
(and the namespace of any template template argument).
Typedef names and using-declarations used to specify the types do not contribute to this set. The sets of namespaces and classes are
determined in the following way:
If T is a fundamental type, [...]
If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its
direct and indirect base classes. Its associated namespaces are the
innermost enclosing namespaces of its associated classes. Furthermore,
if T is a class template specialization, its associated namespaces and
classes also include: the namespaces and classes associated with the
types of the template arguments provided for template type parameters
(excluding template template parameters); the namespaces of which any
template template arguments are members; and the classes of which any
member templates used as template template arguments are members. [
Note: Non-type template arguments do not contribute to the set of associated namespaces. —end note ]
[...]
With template specialization I can get it work:
namespace DoStuffUtilNamespace
{
template<typename UNKNOWN>
void doStuff(UNKNOWN& foo)
{
static_assert(sizeof(UNKNOWN) == -1, "CANNOT USE DEFAULT INSTANTIATION!");
}
}
class UtilForDoingStuff
{
public:
template <typename UNKNOWN>
void doStuffWithObjectRef(UNKNOWN& ref)
{
DoStuffUtilNamespace::doStuff(ref);
}
};
class MyClassThatCanDoStuff { };
namespace DoStuffUtilNamespace
{
using ::MyClassThatCanDoStuff;
template <> void doStuff<MyClassThatCanDoStuff>(MyClassThatCanDoStuff& foo) { /* No assertion! */ }
}
int main()
{
MyClassThatCanDoStuff foo;
DoStuffUtilNamespace::MyClassThatCanDoStuff scoped_foo;
UtilForDoingStuff util;
DoStuffUtilNamespace::doStuff(foo); // Compiles
DoStuffUtilNamespace::doStuff(scoped_foo); // Compiles
util.doStuffWithObjectRef(foo); // Compiles
util.doStuffWithObjectRef(scoped_foo); // Compiles
}
Declarations from namespaces associated with the types of the function arguments both from the instantiation context and from the definition context.
Example with the following code which prints B::foo Demo
namespace A
{
template <typename T>
void foo(const T&) {std::cout << "A::foo" << std::endl;}
template <typename T>
void bar(const T& t) {
foo(t); // thank to ADL, it will also look at B::foo for B::S.
}
}
namespace B
{
struct S {};
void foo(const S&) {std::cout << "B::foo" << std::endl;}
}
int main()
{
B::S s;
A::bar(s);
}
So when calling ?::foo(const B::S&), the second bullet point adds B::foo to the list of overloads.
why template-specialization works in this case
There is only one function:
template<>
void DoStuffUtilNamespace::doStuff<MyClassThatCanDoStuff>(MyClassThatCanDoStuff& foo);
even if it is defined later.
Note that the fact that there is a specialization should be known in the translation unit, else the program is ill formed (doesn't respect ODR).
while overloading doesn't.
You think:
So I think that explains why util.doStuffWithObjectRef(foo); triggers the static assertion: doStuff(MyClassThatCanDoStuff&) hasn't been declared at the point of definition of UtilForDoingStuff::doStuffWithObjectRef<UNKNOWN>(UNKNOWN&). And indeed moving the class UtilForDoingStuff definition after the doStuff overload has been defined seems to fix the issue.
Exactly.

Two-phase function template compilation: not *only* ADL is employed in the 2nd phase?

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.