Подтвердить что ты не робот

Разрешение перегрузки шаблона для операторов внутри анонимного пространства имен

Короткий вопрос: Операторы имеют специальные правила поиска шаблонов для разрешения перегрузки с внутренней связью или это код внизу шаблона ошибки перегрузки шаблона для операторов в GCC?

Детали: вместо вставки фрагмента кода. Я расскажу вам о моих рассуждениях. Начнем с простого кода:

#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);
}

Вышеприведенные отпечатки "hello", все в порядке.

Теперь поставьте оба foo в анонимное пространство имен:

namespace {
template<typename T> 
void foo (const A<T>&a) { foo(a.b); } 
void foo (const B&) { std::cout << "hello"; }
}

Теперь код не компилируется. Кланг говорит error: call to function 'foo' that is neither visible in the template definition nor found by argument-dependent lookup и GCC template argument deduction/substitution failed.

Это не работает, потому что foo(const B&) определяется после foo<T> и не имеет внешней привязки, как описано в n4296:

[basic.link] Неименованное пространство имен или пространство имен, объявленное прямо или косвенно в неназванном пространстве имен, внутренняя связь. Все остальные пространства имен имеют внешнюю связь.

[temp.point] Контекст создания выражения, который зависит от аргументов шаблона, представляет собой набор объявлений с внешней связью, объявленной до момента создания специализированной специализации в той же самой единице перевода.

[temp.dep.candidate] Для вызова функции, где постфиксное выражение является зависимым именем, функции кандидата найдены с использованием обычных правил поиска (3.4.1, 3.4.2), за исключением того, что:

  • Для части поиска, использующей поиск неквалифицированного имени (3.4.1), только объявления функций из шаблона контекст определения.

  • Для части поиска, используя (3.4.2), только объявления функций, найденные в либо контекст определения шаблона, либо экземпляр шаблона контекст.

Теперь то же самое с использованием операторов:

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) теперь совершенно доволен кодом и дает предупреждение 0 -Wall -Wextra -pedantic -ansi, а clang жалуется на 'operator<<' так же, как и на foo.

Я не нашел никакого исключения для поиска перегрузки оператора в стандарте, поэтому я считаю, что это ошибка (функция?) в GCC, но правила разрешения шаблонов не так просто, поэтому я подумал, что могу проверить здесь, прежде чем подавать ошибку.

Вы можете увидеть этот код в режиме реального времени здесь.

4b9b3361

Ответ 1

Это определенно ошибка в gcc. Код ниже правильно печатает right с помощью clang, но wrong с 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;
}

Сообщено здесь.