Ambiguous overload in gcc, fine with msvc

The following code compiles with msvc 18.00, but with a gcc 4.9.1 error:

#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 error message:

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?

+4
source share
1 answer

I don't like to say this, but MSVC is right and gcc 5.1 and clang 3.6 are wrong! To simplify, we call:

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 F1is defined as a better function than another viable function F2if, for all iICS arguments i( F1) no worse conversion scheme than ICS i( F2) and then

  • [...]
  • F1 F2 - , F1 - F2 , 14.5.6.2.

: , / . operator+(Num<A>, Num<B>), operator+(Num<N>, Num<-N>). operator+(Num<C>, Num<-C>), .

, , Num<-N>, , , Num<M>, .

+4

All Articles