首先,请随意为这个问题提出更好的标题。
考虑以下计划:
#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 ++编译器也没有任何错误。
那么,问题是哪些编译器就在这里?这个代码是不是形成了?标准对此有何看法?
答案 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在全局命名空间中查找运算符。