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

Различия в разрешении вызовов шаблона 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 предпочитает не-член, не выдается предупреждение.

4b9b3361

Ответ 1

В соответствии с разрешением перегрузки существуют две жизнеспособные функции: специализация глобальной operator| -template с выведенными аргументами и специализация шаблона функции оператора-члена. Оба имеют одну и ту же подпись - шаблон функции-члена имеет неявный параметр объекта типа Alice const& (см. §13.3.1/4).

Таким образом, обе жизнеспособные функции (после вывода аргумента шаблона) имеют одну и ту же подпись. И ни один из шаблонов, из которых они были созданы, не является более специализированным, чем другой. Так что это действительно двусмысленность и поэтому плохо сформировалась. Удивительно, что VС++ верен.

Как я могу заставить одно и то же поведение?

Возможно, вам следует просто удалить двусмысленность, Clang, VС++ и GCC должны иметь такое же поведение.