这是一个VC++2010编译器错误吗?

Is this a VC++2010 compiler bug?

本文关键字:编译器 VC++2010 错误 一个      更新时间:2023-10-16

Using 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:appsMVS10VCincludevector(609): error C2593: 'operator !=' is ambiguous
1>          C:appsMVS10VCincludexmemory(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:appsMVS10VCincludevector(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

这是一个

错误。

您已经决定为所有类型的类型提供operator!=,这显然会导致与已经定义了此类运算符的类型发生冲突。

在库实现 [1] 中的两个std::allocator<Test>之间解析对operator!=调用期间的参数相关查找允许在尝试查找要使用的operator!=时搜索 Test 的命名空间(以及std [2]。

所以:

  • 在损坏的情况下,该命名空间是全局命名空间,其中还包含匹配的operator!=。现在,这无关紧要,因为命名空间 std 中的函数是更好的匹配 [3];VS 错误是提出了歧义。

  • 但是当Test在命名空间XXX中时(尽管有typedef(,由于上述规则搜索的命名空间反而是命名空间XXX,它不包含operator!=的冲突定义。

无论如何,最好不要像这样为所有类型定义运算符。


[1] 编译器 /库 impl 上的行std::vector<Test> vt2 = std::move(vt);实现的某些部分是调用 bool operator!=<std::allocator<Test>>(const std::allocator<Test>&, const std::allocator<Test>&)

[2] 引文如下:

[C++11: 3.4.2/1]: 当函数调用 (5.2.2( 中的后缀表达式非限定 id 时,可以搜索在通常的非限定查找 (3.4.1( 期间未考虑的其他命名空间,并且在这些命名空间中,可能会找到不可见的命名空间范围友元函数声明 (11.3(。对搜索的这些修改 取决于参数的类型(对于模板模板参数,取决于模板参数的命名空间(。

[C++11: 3.4.2/2]: 对于函数调用中T的每个参数类型,要考虑一组零个或多个关联的命名空间和一组零个或多个关联的类。命名空间和类的集合完全由函数参数的类型(以及任何模板模板参数的命名空间(决定。用于指定类型的 Typedef 名称和 using-声明对此集合没有贡献。命名空间和类集按以下方式确定:

  • [..]
  • 如果T是类类型(包括联合(,则其关联的类是:类本身;它是其成员的类(如果有的话(;及其直接和间接基类。其关联的命名空间是其关联类所属的命名空间。此外,如果T是类模板专用化,则其关联的命名空间和类还包括:与为模板类型参数(不包括模板模板参数(提供的模板参数类型相关联的命名空间和类;任何模板模板参数都是其成员的命名空间;以及用作模板模板参数的任何成员模板都是其成员的类。[ 注意:非类型模板参数对关联命名空间集没有贡献。—尾注 ]
  • [..]

[3] 引文如下:

[C++11: 13.3.3/1]: 给定这些定义,一个可行函数F1被定义为比另一个可行函数更好的函数F2如果对于所有参数iICSi(F1)不是比ICSi(F2)更差的转换序列,然后:

  • [..]
  • F1F2是函数模板专用化,根据 14.5.6.2 中所述的部分排序规则,F1 的函数模板比F2模板更专用。

[C++11: 14.5.6.2/2]: 部分排序通过依次转换每个模板(请参阅下一段(并使用函数类型执行模板参数推导来选择两个函数模板中哪个比另一个更专业。演绎过程确定其中一个模板是否比另一个模板更专业。如果是这样,则更专业的模板是部分排序过程选择的模板。

我的解释是,这个过程决定了std中的函数比全局命名空间中的函数"更专业",所以实际上不应该有歧义。


感谢@BoPersson和@DavidRodríguez对这个踢屁股答案的宝贵贡献。