Различия в разрешении вызовов шаблона GCC и Clang
С учетом следующего кода:
#include <iostream>
struct Alice
{
template <typename A>
void operator|(const A& /*a*/) const
{
std::cout << "operator| member" << std::endl;
}
};
template <typename A>
void operator|(const A& /*a*/, const Alice& /*alice*/)
{
std::cout << "operator| non-member" << std::endl;
}
int main()
{
Alice a;
Alice b;
a | b;
return 0;
}
Он компилируется без предупреждения как с GCC 4.8.1, 4.9, так и с clang 3.4, но дает разные результаты.
$ g++ -Wall -Wextra -std=c++11 alice.cpp && ./a.out
operator| non-member
$ clang++ -Wall -Wextra -std=c++11 alice.cpp && ./a.out
operator| member
В чем причина этой разницы? Как я могу заставить одно и то же поведение?
EDIT: Интересный факт: удаление классификатора const
из функции-члена делает gcc также предпочтительной для функции-члена. Однако это не решает проблему.
EDIT: clang++ предпочитает не-член вместо этого, если -std=c++11
не указан.
РЕДАКТИРОВАТЬ: ICC 14.0 предпочитает не-член, не выдается предупреждение.
Ответы
Ответ 1
В соответствии с разрешением перегрузки существуют две жизнеспособные функции: специализация глобальной operator|
-template с выведенными аргументами и специализация шаблона функции оператора-члена. Оба имеют одну и ту же подпись - шаблон функции-члена имеет неявный параметр объекта типа Alice const&
(см. §13.3.1/4).
Таким образом, обе жизнеспособные функции (после вывода аргумента шаблона) имеют одну и ту же подпись. И ни один из шаблонов, из которых они были созданы, не является более специализированным, чем другой. Так что это действительно двусмысленность и поэтому плохо сформировалась. Удивительно, что VС++ верен.
Как я могу заставить одно и то же поведение?
Возможно, вам следует просто удалить двусмысленность, Clang, VС++ и GCC должны иметь такое же поведение.