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

mcm*_*mcc 6 c++ visual-studio-2010 visual-c++ visual-c++-2010 c++11

使用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;
}
Run Code Online (Sandbox Code Playgroud)

如果我按原样编译上面的代码,它会失败并显示以下错误:

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>          ]
Run Code Online (Sandbox Code Playgroud)

... vector(609)解析到这一行:

        else if (get_allocator() != _Right.get_allocator())
Run Code Online (Sandbox Code Playgroud)

OTOH,如果我取消注释namespace XXX相关的行,它会毫无怨言地编译.

我不得不认为这是一个编译器错误,但我正在寻找一些独立的验证.

编辑: 只是作为解释,我第一次使用VS2010重新编译一些旧代码时遇到了这种情况.全球运营商从过去几年开始(现已删除).我只是无法理解为什么有些代码失败而其他代码没有.上面的代码是我对失败案例的精炼(显然,旧代码不包含调用std::move()).

更新:我记录了一个MS的错误,他们回答说这已经修复了"在编译器的下一个版本中" - 我认为这意味着Visual C++ 11.请参阅:http://connect.microsoft.com/VisualStudio/feedback/细节/ 731692 /回归涉及-全球运营商和-STD-矢量

Lig*_*ica 10

这是一个错误.

你已经决定提供operator!=所有类型的永远这显然会导致与已经有这样的运营商定义的类型冲突.

在解决对库实现[1]operator!=两个std::allocator<Test>s 之间的调用期间的参数依赖查找允许在尝试查找要使用时[2]搜索(以及)命名空间.Teststdoperator!=

所以:

  • 在你破碎的情况下,该命名空间是全局命名空间,它也包含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)中的postfix-expression是非限定id时,可以搜索在通常的非限定查找(3.4.1)期间未考虑的其他名称空间,并在这些名称空间中搜索名称空间范围的朋友函数声明( 11.3)可能没有其他可见的.对搜索的这些修改取决于参数的类型(以及模板模板参数,模板参数的命名空间).

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

  • [..]
  • 如果T是类类型(包括联合),则其关联的类是:类本身; 它所属的成员,如果有的话; 及其直接和间接基类.其关联的名称空间是其关联类是成员的名称空间.此外,如果T是类模板特化,则其关联的名称空间和类还包括:与模板类型参数(模板模板参数除外)提供的模板参数类型相关联的名称空间和类 ; 任何模板模板参数都是成员的名称空间; 以及用作模板模板参数的任何成员模板的类都是成员.[ 注意:非类型模板参数不会对关联的命名空间集合产生影响.- 尾注 ]
  • [..]

[3] 引文如下:

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

  • [..]
  • F1并且F2是函数模板特化,并且函数模板F1F2根据14.5.6.2中描述的部分排序规则的模板更专业.

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

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


感谢@BoPersson和@DavidRodríguez为您的回答做出的宝贵贡献.

  • 比较的值是`std :: allocator <Test>`类型.这会将`Test`的命名空间添加到ADL搜索中(除了`std`). (2认同)
  • @LightnessRacesinOrbit:不,它不应该.错误消息说它无法在两个选项中做出决定,并且我认为应用用户定义的标准中定义的部分顺序比库中的那个更少*,因此编译器应该选择那个起来.我越来越相信有一个但是在实现中,它不是为什么它考虑模板,而是为什么它认为它*和另一个一样好. (2认同)