12

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

+0

Возможно, связано с https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51577 –

+0

Спасибо, что указали, что вне @ T.C. В GCC действительно есть что-то нелепое. Я все равно буду записывать его как отдельную ошибку, так как контекст кажется достаточно разным, но основная причина может быть одинаковой. – Thibaut

+0

Я получил код 'foo' для компиляции на GCC, переключив порядок определений. Соответствует ли это тому, что вы ожидаете? Вот [моя вилка вашего живого примера] (http://ideone.com/HBFsO4). –

ответ

2

Это определенно ошибка в 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; 
} 

Отмечено here.

Смежные вопросы