以下代码使用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, ":(");
^
哪种编译器是对的?
答案 0 :(得分:4)
我不想这样说,但是MSVC是对的,gcc 5.1和clang 3.6是错的!为简化起见,我们致电:
operator+(Num<1>, Num<-1>)
有重载:
operator+(Num<N>, Num<M>)
operator+(Num<N>, Num<-N>)
两者显然都是可行的候选人。而且,根据[over.match.best]:
鉴于这些定义,可行函数
F1
被定义为比另一个可行函数更好的函数F2
如果对于所有参数i
,ICS i (F1
)并不比ICS <更差的转换序列em> i (F2
),然后
- [...]
F1
和F2
是功能模板专精,F1
的功能模板更专业 根据14.5.6.2中描述的部分排序规则,F2
的模板。
确定部分排序的规则归结为:对于每个模板参数,合成一个新的类型/值并尝试用它调用另一个重载。对于第一次重载,它变为operator+(Num<A>, Num<B>)
,您无法调用operator+(Num<N>, Num<-N>)
。但是,第二个重载变为operator+(Num<C>, Num<-C>)
,可以调用第一个重载。
因此,过载Num<-N>
比过载Num<M>
更专业,因此应该明确地优先考虑。