Reputation: 3460
The following code compiles fine with msvc 18.00 but fails with gcc 4.9.1:
#include <type_traits>
template <int N> class Num { };
class Zero { };
template <int N, int M>
Num<N + M> operator+(Num<N>, Num<M>)
{
return {};
}
template <int N>
Zero operator+(Num<N>, Num<-N>)
{
return {};
}
int main()
{
Num<1> one;
Num<-1> mone;
Num<0> null;
auto a = one + one;
static_assert(std::is_same<decltype(a), Num<2>>::value, ":(");
auto b = one + mone;
static_assert(std::is_same<decltype(b), Zero>::value, ":(");
auto c = null + null;
static_assert(std::is_same<decltype(c), Zero>::value, ":(");
}
gcc's error message is:
ambiguous.cpp: In function 'int main()':
ambiguous.cpp:28:16: error: ambiguous overload for 'operator+' (operand types are 'Num<1>' and 'Num<-1>')
auto b = one + mone;
^
ambiguous.cpp:28:16: note: candidates are:
ambiguous.cpp:8:12: note: Num<(N + M)> operator+(Num<N>, Num<M>) [with int N = 1; int M = -1]
Num<N + M> operator+(Num<N>, Num<M>)
^
ambiguous.cpp:14:6: note: Zero operator+(Num<N>, Num<(- N)>) [with int N = 1]
Zero operator+(Num<N>, Num<-N>)
^
ambiguous.cpp:29:47: error: template argument 1 is invalid
static_assert(std::is_same<decltype(b), Zero>::value, ":(");
^
ambiguous.cpp:31:17: error: ambiguous overload for 'operator+' (operand types are 'Num<0>' and 'Num<0>')
auto c = null + null;
^
ambiguous.cpp:31:17: note: candidates are:
ambiguous.cpp:8:12: note: Num<(N + M)> operator+(Num<N>, Num<M>) [with int N = 0; int M = 0]
Num<N + M> operator+(Num<N>, Num<M>)
^
ambiguous.cpp:14:6: note: Zero operator+(Num<N>, Num<(- N)>) [with int N = 0]
Zero operator+(Num<N>, Num<-N>)
^
ambiguous.cpp:32:47: error: template argument 1 is invalid
static_assert(std::is_same<decltype(c), Zero>::value, ":(");
^
Which compiler is right?
Upvotes: 4
Views: 458
Reputation: 302748
I hate to say this, but MSVC is right and gcc 5.1 and clang 3.6 are wrong! To simplify, we're calling:
operator+(Num<1>, Num<-1>)
with overloads:
operator+(Num<N>, Num<M>)
operator+(Num<N>, Num<-N>)
Both are obviously viable candidates. And, according to [over.match.best]:
Given these definitions, a viable function
F1
is defined to be a better function than another viable functionF2
if for all argumentsi
, ICSi(F1
) is not a worse conversion sequence than ICSi(F2
), and then
- [...]
F1
andF2
are function template specializations, and the function template forF1
is more specialized than the template forF2
according to the partial ordering rules described in 14.5.6.2.
The rules for determining partial ordering boil down to: for every template parameter, synthesize a new type/value and try to call the other overload with it. For the first overload, it becomes operator+(Num<A>, Num<B>)
, with which you cannot call operator+(Num<N>, Num<-N>)
. However, the second overload becomes operator+(Num<C>, Num<-C>)
, with which you can call the first overload.
Thus, the overload taking Num<-N>
is more specialized than the overload taking Num<M>
, so it should be preferred unambiguously.
Upvotes: 4