Short question: do operators have special template lookup rules for overload resolution with internal linkage or is the code at the bottom a template overload resolution bug for operators in GCC?
The detail: instead of pasting a chunk of code I'll take you through my reasoning. Let's start with some simple code:
#include <iostream>
template<typename T> struct A{ T b; };
struct B{};
template<typename T>
void foo (const A<T>&a) { foo(a.b); }
void foo (const B&) { std::cout << "hello"; }
int main(){
A<B> b;
foo(b);
}
The above prints "hello"
, everything is fine.
Now let's put both foo
in an anonymous namespace:
namespace {
template<typename T>
void foo (const A<T>&a) { foo(a.b); }
void foo (const B&) { std::cout << "hello"; }
}
The code now fails to compile. Clang says error: call to function 'foo' that is neither visible in the template definition nor found by argument-dependent lookup
and GCC template argument deduction/substitution failed
.
This is broken because foo(const B&)
is defined after foo<T>
and doesn't have an external linkage, as explained in n4296:
[basic.link] An unnamed namespace or a namespace declared directly or indirectly within an unnamed namespace has internal linkage. All other namespaces have external linkage.
[temp.point] 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.
[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 (3.4.1, 3.4.2) except that:
For the part of the lookup using unqualified name lookup (3.4.1), only function declarations from the template definition context are found.
For the part of the lookup using associated namespaces (3.4.2), only function declarations found in either the template definition context or the template instantiation context are found.
Now the same thing using operators:
struct ostream {} cout;
template<typename T> struct A{ T t; };
struct B{};
namespace {
template<typename T>
ostream& operator<< (ostream& out, const A<T>&v)
{ return out << v.t; }
ostream& operator<< (ostream& out, const B&)
{ return out; }
}
int main(){
A<B> a;
cout << a;
}
GCC (4.7/4.8/4.9) is now perfectly happy with the code and gives zero warning with -Wall -Wextra -pedantic -ansi
while clang complains about 'operator<<'
the same way it did for foo
.
I didn't find any exception for operator overload lookup in the standard so I believe this is a bug (feature?) in GCC but template resolution rules are not easy so I thought I might check here before filing a bug.
You can see this code live here.
This is definitely a bug in gcc. The code below rightly prints right
with clang but wrong
with GCC.
#include <iostream>
template<typename T> struct A{ T t; };
struct B{};
struct C : public B{};
std::ostream& operator<< (std::ostream& out, const B&)
{ return out << "right"; }
namespace {
template<typename T>
std::ostream& operator<< (std::ostream& out, const A<T>&v)
{ return out << v.t; }
std::ostream& operator<< (std::ostream& out, const C&)
{ return out << "wrong"; }
}
int main(){
A<C> a;
std::cout << a;
}
Reported here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With