Короткий вопрос: У операторов есть специальные правила поиска шаблонов для разрешения перегрузки с внутренней связью или код внизу шаблона ошибки перегрузки для операторов в 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) теперь совершенно счастлив с кодом и дает нулевой предупреждение с -Wall -Wextra -pedantic -ansi
в то время как лязг жалуется 'operator<<'
так же, как для foo
.
Я не нашел никакого исключения для поиска перегрузки оператора в стандарте, поэтому я считаю, что это ошибка (функция?) В GCC, но правила разрешения шаблонов не так просто, поэтому я подумал, что могу проверить здесь, прежде чем регистрировать ошибку.
Вы можете увидеть этот код в реальном времени here.
Возможно, связано с https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51577 –
Спасибо, что указали, что вне @ T.C. В GCC действительно есть что-то нелепое. Я все равно буду записывать его как отдельную ошибку, так как контекст кажется достаточно разным, но основная причина может быть одинаковой. – Thibaut
Я получил код 'foo' для компиляции на GCC, переключив порядок определений. Соответствует ли это тому, что вы ожидаете? Вот [моя вилка вашего живого примера] (http://ideone.com/HBFsO4). –