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

时间:2012-03-15 21:47:57

标签: c++ visual-studio-2010 visual-c++ c++11 visual-c++-2010

使用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.请参阅:{{3} }

1 个答案:

答案 0 :(得分:10)

这是一个错误。

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

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

所以:

    在你破碎的情况下,
  • 该命名空间是全局命名空间,它还包含匹配的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 unqualified-id 时,其他名称空间可以搜索在通常的非限定查找(3.4.1)期间未被考虑的,并且在那些名称空间中,可以找到名称空间范围的朋友函数声明(11.3),否则不可见。 对搜索的这些修改   取决于参数的类型(对于模板模板参数,模板参数的命名空间)。

     

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

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

[3] 引文如下:

  

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

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

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

我的解释是,这个过程确定std中的函数比全局命名空间中的函数“更专业”,所以实际上不应该是歧义。 < / p>


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