gcc中的歧义重载,在msvc中正常

4
以下代码在msvc 18.00编译正常,但在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的错误信息为:

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, ":(");
                                               ^

哪个编译器是正确的?


不是答案,但你可以通过删除第二个重载并将 Zero 更改为 using Zero = Num<0>; 来消除这种歧义。 - TartanLlama
@TartanLlama 这只是玩具代码,用来说明问题。 - MadScientist
1个回答

4

我很不想说,但是MSVC是正确的,而gcc 5.1clang 3.6是错误的!简单来说,我们正在调用:

operator+(Num<1>, Num<-1>)

使用重载:

operator+(Num<N>, Num<M>)
operator+(Num<N>, Num<-N>)

两者都是可行的候选项。根据[over.match.best]:给定这些定义,如果对于所有参数i,ICSi(F1)不是比ICSi(F2)更差的转换序列,则定义一个可行的函数F1比另一个可行的函数F2更好,然后[...]F1和F2是函数模板特化,F1的函数模板比14.5.6.2中描述的模板更加专业。

  • [...]

确定部分排序的规则归结为:对于每个模板参数,合成新的类型/值并尝试使用它来调用另一个重载。 对于第一个重载,它变成了operator+(Num, Num),您不能使用它来调用operator+(Num, Num<-N>)。 但是,第二个重载变成了operator+(Num, Num<-C>),您可以使用它来调用第一个重载。
因此,采用Num<-N>的重载比采用Num的重载更加专业,因此应无歧义地选择它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接