Это ошибка компилятора VC ++ 2010? - PullRequest
6 голосов
/ 16 марта 2012

Использование Visual Studio 2010 SP1:

#include <vector>

//namespace XXX {
  struct Test
  {
    bool operator==(const Test& r) const  { return true; }
  };
//}
//typedef XXX::Test Test;

template <typename T> inline bool operator!=(const T& l,const T& r) 
{ return !(l==r); }

int main()
{
  std::vector<Test> vt;
  std::vector<Test> vt2 = std::move(vt);
  return 0;
}

Если я скомпилирую приведенный выше код как есть, произойдет сбой с этой ошибкой:

1>C:\apps\MVS10\VC\include\vector(609): error C2593: 'operator !=' is ambiguous
1>          C:\apps\MVS10\VC\include\xmemory(268): could be 'bool std::operator !=<_Ty,_Ty>(const std::allocator<_Ty> &,const std::allocator<_Ty> &) throw()'
1>          with
1>          [
1>              _Ty=Test
1>          ]
1>          test.cpp(11): or       'bool operator !=<std::allocator<_Ty>>(const T &,const T &)' [found using argument-dependent lookup]
1>          with
1>          [
1>              _Ty=Test,
1>              T=std::allocator<Test>
1>          ]
1>          while trying to match the argument list '(std::allocator<_Ty>, std::allocator<_Ty>)'
1>          with
1>          [
1>              _Ty=Test
1>          ]
1>          C:\apps\MVS10\VC\include\vector(606) : while compiling class template member function 'void std::vector<_Ty>::_Assign_rv(std::vector<_Ty> &&)'
1>          with
1>          [
1>              _Ty=Test
1>          ]

... где vector(609) разрешается в этой строке:

        else if (get_allocator() != _Right.get_allocator())

OTOH, если я раскомментирую строки, связанные с namespace XXX, он компилируется без жалоб.

Я должен думать, что это ошибка компилятора, но мне нужна независимая проверка.

РЕДАКТИРОВАТЬ: Просто для пояснения я столкнулся с этой ситуацией, когда впервые перекомпилировал старый код с VS2010. Глобальный оператор был чем-то напрасным из прошлых лет (теперь удален). Я просто не мог понять, почему некоторые коды не работают, а другие нет. Приведенный выше код - это мой пример неудачного случая (очевидно, что старый код не будет содержать вызовов std::move()).

ОБНОВЛЕНИЕ: Я зарегистрировал ошибку в MS, и они ответили, что это исправлено «в следующем выпуске компилятора» - что, я полагаю, означает Visual C ++ 11. См .: http://connect.microsoft.com/VisualStudio/feedback/details/731692/regression-involving-global-operator-and-std-vector

1 Ответ

10 голосов
/ 16 марта 2012

Это ошибка.

Вы решили предоставить operator!= для всех типов , что, очевидно, вызовет конфликты с типами, для которых уже определен такой оператор.

Argument Dependent Lookup во время разрешения вызова к operator!= между двумя std::allocator<Test> s внутри вашей реализации библиотеки [1] позволяет искать пространство имен Test (а также std) при попытке найти operator!= для использования [2] .

Итак:

  • в вашем случае с ошибками это пространство имен является глобальным пространством имен, которое также содержит operator!=, которое соответствует. Теперь это не должно иметь значения, потому что функция в пространстве имен std лучше соответствует [3] ; ошибка VS в том, что вместо этого возникает неоднозначность.

  • , но когда вместо Test находится пространство имен XXX (несмотря на typedef), пространство имен, искомое из-за вышеуказанного правила, вместо этого является пространством имен XXX, которое не содержит конфликтующего определения для operator!=.

Лучше всего не определять операторы для всех типов когда-либо в любом случае.


[1] Некоторая часть реализации вашей строки std::vector<Test> vt2 = std::move(vt); на вашем компиляторе / библиотеке impl вызывает bool operator!=<std::allocator<Test>>(const std::allocator<Test>&, const std::allocator<Test>&).

[2] Цитаты следуют:

[C++11: 3.4.2/1]: Когда постфиксное выражение в вызове функции (5.2.2) представляет собой unqualified-id , другие пространства имен не учитываются во время обычный неквалифицированный поиск (3.4.1) может быть найден , и в этих пространствах имен могут быть найдены объявления дружественных функций (11.3) в области имен пространства имен, которые не видны иным образом. Эти модификации для поиска зависит от типов аргументов (а для аргументов шаблона шаблона - пространство имен аргумента шаблона).

[C++11: 3.4.2/2]: Для каждого типа аргумента T в вызове функции существует набор из нуля или более связанных пространств имен и набор из нуля или более связанных классов для считаться . Наборы пространств имен и классов полностью определяются типами аргументов функции (и пространством имен любого аргумента шаблона шаблона). Имена типов и using-декларации , используемые для указания типов, не участвуют в этом наборе. Наборы пространств имен и классов определяются следующим образом:

  • [..]
  • Если T является типом класса (включая объединения), его ассоциированными классами являются: сам класс; класс, членом которого он является, если таковой имеется; и его прямые и косвенные базовые классы. Его связанные пространства имен являются пространствами имен, членами которых являются связанные классы. Кроме того, , если T является специализацией шаблона класса, к связанным с ним пространствам имен и классам также относятся: пространства имен и классы, связанные с типами аргументов шаблона, предоставляемых для параметров типа шаблона (исключая параметры шаблона шаблона); пространства имен, членами которых являются любые аргументы шаблона; и классы, членами которых являются любые шаблоны-члены, используемые в качестве аргументов шаблона. [ Примечание: Нетипичные аргументы шаблона не вносят вклад в набор связанных пространств имен. - Конечная заметка ]
  • [..]

[3] Цитаты следуют:

[C++11: 13.3.3/1]: С учетом этих определений жизнеспособная функция F1 определена как лучшая функция, чем другая жизнеспособная функция F2, если для всех аргументов i, ICSi(F1) не является худшей последовательностью преобразования чем ICSi(F2), а затем:

  • [..]
  • F1 и F2 являются специализациями шаблонов функций, а шаблон функции для F1 более специализирован, чем шаблон для F2 в соответствии с правилами частичного упорядочения, описанными в 14.5.6.2.

[C++11: 14.5.6.2/2]: Частичное упорядочение выбирает, какой из двух шаблонов функций является более специализированным, чем другой, путем преобразования каждого шаблона по очереди (см. Следующий абзац) и выполнения вывода аргумента шаблона с использованием типа функции. Процесс вывода определяет, является ли один из шаблонов более специализированным, чем другой. Если это так, то более специализированный шаблон выбирается в процессе частичного заказа.

Моя интерпретация состоит в том, что этот процесс определяет, что функция в std "более специализирована", чем функция в глобальном пространстве имен, поэтому на самом деле не должно быть неоднозначности.


Спасибо @BoPersson и @ DavidRodríguez за ваш ценный вклад в этот обалденный ответ.

...