Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template overload resolution for operators inside an anonymous namespace

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.

like image 517
Thibaut Avatar asked Mar 06 '15 17:03

Thibaut


1 Answers

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.

like image 186
Thibaut Avatar answered Sep 27 '22 20:09

Thibaut