Является ли 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++ действительно плохо себя ведет здесь?
Ответы
Ответ 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