First of all feel free to suggest better title for this question.
Consider following program:
#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: Compile successfully ( See live demo here )
clang++
3.8.0 ( See live demo here )
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.
Microsoft Visual C++
19.00.23506 ( See live demo here )
Error(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.
This program surprisingly compiles without any error on Intel C++ compiler also.
So, the question is which compilers are right here ? Is this code ill formed ? What does the standard says about this ?
Like John Zwinck said, put the operator into namespace N
. The reason being that ADL considers only the the innermost enclosing namespace of the class in question.
From [basic.lookup.argdep]/2, emphasis mine:
For each argument type T in the function call, there is a set of zero or more associated namespaces and a set of zero or more associated classes to be considered. The sets of namespaces and classes are determined entirely by the types of the function arguments (and the namespace of any template template argument). Typedef names and using-declarations used to specify the types do not contribute to this set. The sets of namespaces and classes are determined in the following way:
- [...]
- If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the innermost enclosing namespaces of its associated classes. Furthermore, if T is a class template specialization, its associated namespaces and classes also include: the namespaces and classes associated with the types of the template arguments provided for template type parameters (excluding template template parameters); the namespaces of which any template template arguments are members; and the classes of which any member templates used as template template arguments are members. [ Note: Non-type template arguments do not contribute to the set of associated namespaces. — end note ]
With only a special exception to be made if that namespace is an inline namespace.
If an associated namespace is an inline namespace, its enclosing namespace is also included in the set. If an associated namespace directly contains inline namespaces, those inline namespaces are also included in the set.
So your operator+
should not be found by ADL, and as such should not participate in overload resolution inside std::accumulate
.