Короткий вопрос: Операторы имеют специальные правила поиска шаблонов для разрешения перегрузки с внутренней связью или это код внизу шаблона ошибки перегрузки шаблона для операторов в 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, но правила разрешения шаблонов не так просто, поэтому я подумал, что могу проверить здесь, прежде чем подавать ошибку.
Вы можете увидеть этот код в режиме реального времени здесь.