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

Является ли g++ неверным с перегрузкой шаблона функции?

Я взял следующий пример из http://en.cppreference.com/w/cpp/language/function_template#Function_template_overloading и clang (3.4), кажется, обрабатывает его просто отлично, а g++ (4.8.3) дает ошибку "неоднозначной перегрузки":

struct A {};
template<class T> struct B {
  template<class R> void operator*(R&){ cout << "1" << endl; }             // #1
};
template<class T, class R> void operator*(T&, R&) { cout << "2" << endl;}  // #2
int main() {
  A a;
  B<A> b;
  b * a; //prints 1
}

clang правильно печатает 1 (как и ожидалось в соответствии с cppreference), а g++ дает эту ошибку:

test_templates.cpp: In function ‘int main()’:
test_templates.cpp:13:5: error: ambiguous overload for ‘operator*’ (operand types are ‘B<A>’ and ‘A’)
   b * a; //prints 1
     ^
test_templates.cpp:13:5: note: candidates are:
test_templates.cpp:7:26: note: void B<T>::operator*(R&) [with R = A; T = A]
   template<class R> void operator*(R&){ cout << "1" << endl; }            // #1
                          ^
test_templates.cpp:9:33: note: void operator*(T&, R&) [with T = B<A>; R = A]
 template<class T, class R> void operator*(T&, R&) { cout << "2" << endl;}  // #2

Неужели g++ действительно плохо себя ведет здесь?

4b9b3361

Ответ 1

Этот пример берется из стандарта (это черновик для С++ 11).

14.5.6.2 Частичное упорядочение шаблонов функций пример 3:

struct A { };
template<class T> struct B {
  template<class R> int operator*(R&); // #1
};
template<class T, class R> int operator*(T&, R&); // #2
// The declaration of B::operator* is transformed into the equivalent of
// template<class R> int operator*(B<A>&, R&); // #1a
int main() {
  A a;
  B<A> b;
  b * a; // calls #1a
}

Итак, сам стандарт в значительной степени говорит, что это юридический код. Я мог бы скопировать правила вставки, но можно также щелкнуть ссылку и перейти к соответствующему месту. Моя точка зрения заключается только в том, чтобы доказать, что это правильный компилируемый код, определенный стандартом.

Для того, что стоило на моем debian clang 3.5.0, скомпилировало его сразу, clang 3.4.2 пришлось выполнять с -std=c++11, g++ 4.9.1 сообщалось о двусмысленности во всех случаях (я даже пытался 1y).

Я озадачен поведением clang. Я думал, что это могло быть неоднозначным в более ранних версиях С++, правило для устранения неоднозначности было добавлено как часть С++ 11, а g++ не поддерживалось. Но clang 3.5 скомпилирует его даже с помощью -std=c++98.

Ответ 2

Этот вызов является ambiguos. GCC прав.

§13.5.2/1
Таким образом, для любого двоичного оператора @, x @y могут быть x.operator @(y) или operator @(x, y). Если обе формы оператора функция объявила правила в 13.3.1.2, которые определяют, какие, используется интерпретация.

И в этом случае у нас есть как функция-член, так и не-член. Встроенная версия не включена, поскольку левый оператор имеет тип класса.

Если вы вызываете оператор явно, не будет никакой двусмысленности. Однако, когда вызов выполняется через оператор (таким образом, неявно) , нет ничего, что могло бы различать член и не-член, поэтому они являются жизнеспособными функциями, которые в этом случае приводят к неоднозначной функции звоните.

Предыдущие версии clang сообщают об этом как о двусмысленном: http://goo.gl/OWsJUv