不同编译器之间的名称查找不一致

时间:2018-02-08 12:22:58

标签: c++ operator-overloading language-lawyer overload-resolution name-lookup

首先,请随意为这个问题提出更好的标题。

考虑以下计划:

#include <numeric>
namespace N { class C {}; }
int operator+( int i, N::C ) { return i+1; }
int main() {
     N::C a[10];
     std::accumulate( a, a+10, 0 );
} 

g++ 5.4.0:成功编译(参见实时演示here

clang++ 3.8.0(参见实时演示here

错误(S):

In file included from source_file.cpp:3:
/usr/include/c++/v1/numeric:75:25: error: invalid operands to binary expression ('int' and 'N::C')
        __init = __init + *__first;
                 ~~~~~~ ^ ~~~~~~~~
source_file.cpp:8:11: note: in instantiation of function template specialization 'std::__1::accumulate<N::C *, int>' requested here
     std::accumulate( a, a+10, 0 );
          ^
/usr/include/c++/v1/iterator:640:1: note: candidate template ignored: could not match 'reverse_iterator<type-parameter-0-0>' against 'N::C'
operator+(typename reverse_iterator<_Iter>::difference_type __n, const reverse_iterator<_Iter>& __x)
^
/usr/include/c++/v1/iterator:1044:1: note: candidate template ignored: could not match 'move_iterator<type-parameter-0-0>' against 'N::C'
operator+(typename move_iterator<_Iter>::difference_type __n, const move_iterator<_Iter>& __x)
^
/usr/include/c++/v1/iterator:1400:1: note: candidate template ignored: could not match '__wrap_iter<type-parameter-0-0>' against 'N::C'
operator+(typename __wrap_iter<_Iter>::difference_type __n,
^
1 error generated.

Microsoft Visual C++ 19.00.23506(见实时演示here

错误(S):

C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\numeric(20): error C2672: 'operator __surrogate_func': no matching overloaded function found
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\numeric(30): note: see reference to function template instantiation '_Ty std::_Accumulate<_Iter,_Ty,_Fn2>(_InIt,_InIt,_Ty,_Fn2)' being compiled
        with
        [
            _Ty=int,
            _Iter=N::C *,
            _Fn2=std::plus<void>,
            _InIt=N::C *
        ]
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\numeric(38): note: see reference to function template instantiation '_Ty std::accumulate<_InIt,_Ty,std::plus<void>>(_InIt,_InIt,_Ty,_Fn2)' being compiled
        with
        [
            _Ty=int,
            _InIt=N::C *,
            _Fn2=std::plus<void>
        ]
source_file.cpp(8): note: see reference to function template instantiation '_Ty std::accumulate<N::C*,int>(_InIt,_InIt,_Ty)' being compiled
        with
        [
            _Ty=int,
            _InIt=N::C *
        ]
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\numeric(20): error C2893: Failed to specialize function template 'unknown-type std::plus<void>::operator ()(_Ty1 &&,_Ty2 &&) const'
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\numeric(20): note: With the following template arguments:
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\numeric(20): note: '_Ty1=int &'
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\numeric(20): note: '_Ty2=N::C &'

    Error(s):
    In file included from source_file.cpp:3:
    /usr/include/c++/v1/numeric:75:25: error: invalid operands to binary expression ('int' and 'N::C')
            __init = __init + *__first;
                     ~~~~~~ ^ ~~~~~~~~
    source_file.cpp:8:11: note: in instantiation of function template specialization 'std::__1::accumulate<N::C *, int>' requested here
         std::accumulate( a, a+10, 0 );
              ^
    /usr/include/c++/v1/iterator:640:1: note: candidate template ignored: could not match 'reverse_iterator<type-parameter-0-0>' against 'N::C'
    operator+(typename reverse_iterator<_Iter>::difference_type __n, const reverse_iterator<_Iter>& __x)
    ^
    /usr/include/c++/v1/iterator:1044:1: note: candidate template ignored: could not match 'move_iterator<type-parameter-0-0>' against 'N::C'
    operator+(typename move_iterator<_Iter>::difference_type __n, const move_iterator<_Iter>& __x)
    ^
    /usr/include/c++/v1/iterator:1400:1: note: candidate template ignored: could not match '__wrap_iter<type-parameter-0-0>' against 'N::C'
    operator+(typename __wrap_iter<_Iter>::difference_type __n,
    ^
    1 error generated.

这个程序令人惊讶地编译而且英特尔C ++编译器也没有任何错误。

那么,问题是哪些编译器就在这里?这个代码是不是形成了?标准对此有何看法?

2 个答案:

答案 0 :(得分:3)

就像John Zwinck所说的那样,将操作符放入namespace N。原因是ADL只考虑了相关类的最内层封闭命名空间。

来自[basic.lookup.argdep]/2,强调我的:

  

对于函数调用中的每个参数类型T,都有一组零   或更多关联的命名空间以及一组零关联或更多关联   要考虑的课程。命名空间和类的集合是   完全由函数参数的类型决定(和   任何模板模板参数的命名空间)。 Typedef名称和   用于指定类型的using声明对此没有贡献   组。命名空间和类的集合在中确定   以下方式:

     
      
  • [...]
  •   
  • 如果T是类类型(包括联合),则其关联的类是:类本身;它所属的成员,如果有的话;和它的   直接和间接基类。 其关联的命名空间是   其关联类的最内层封闭命名空间。   此外,如果T是类模板特化,则其关联   名称空间和类还包括:名称空间和类   与提供的模板参数的类型相关联   模板类型参数(模板模板参数除外);该   任何模板模板参数都是成员的名称空间;和   任何成员模板用作模板模板的类   参数是成员。 [注意:非类型模板参数不会   贡献于一组相关的命名空间。 - 尾注]
  •   

如果该命名空间是内联命名空间,则只会发生特殊异常。

  

如果关联的命名空间是内联命名空间,则其封闭   命名空间也包含在集合中。如果是关联的命名空间   直接包含内联命名空间,那些内联命名空间也是   包括在集合中。

因此,ADL不应找到您的operator+,因此不应参与std::accumulate内的重载解析。

答案 1 :(得分:2)

如果将operator +放入名称空间N,它就可以工作。通常应该这样做,因为ADL可以帮助您解析运算符。

Clang 5甚至明确告诉你应该这样做。

您可能认为ADL将导致在全局命名空间中进行查找,因为其中一个参数是int。但事实并非如此,因为:

  

1)对于基本类型的参数,关联的命名空间和类集是空的

也就是说,int之类的类型不会导致ADL在全局命名空间中查找运算符。

见这里:http://en.cppreference.com/w/cpp/language/adl