选择哪个类模板特化的首选规则包括将特化重写为函数模板,并通过函数模板[temp.class.order]的排序规则确定哪个函数模板更加专业化 . 考虑这个例子,然后:
#include <iostream>
template <class T> struct voider { using type = void; };
template <class T> using void_t = typename voider<T>::type;
template <class T, class U> struct A { };
template <class T> int foo(A<T, void_t<T>> ) { return 1; }
template <class T> int foo(A<T*, void> ) { return 2; }
int main() {
std::cout << foo(A<int*, void>{});
}
gcc和clang都在这里打印 2
. 这对前面的一些例子很有意义 - 根据非演绎的上下文( void
对 void_t<T>
)进行推断只是被忽略,因此对 <X*, void>
推断 <T, void_t<T>>
成功,但在两个参数中对 <Y, void_t<Y>>
进行推断 <T*, void>
失败 . 精细 .
现在考虑这种概括:
#include <iostream>
template <class T> struct voider { using type = void; };
template <class T> using void_t = typename voider<T>::type;
template <int I> struct int_ { static constexpr int value = I; };
template <class T, class U> struct A : int_<0> { };
template <class T> struct A<T, void_t<T>> : int_<1> { };
template <class T> struct A<T*, void> : int_<2> { };
int main() {
std::cout << A<int*, void>::value << '\n';
}
clang和gcc都报告这个专业化是不明确的,在 1
和 2
之间 . 但为什么?合成的函数模板不是这两种情况之间的区别吗?
1 回答
Clang is being GCC-compatible (并与依赖于这两种行为的现有代码兼容) .
考虑 [temp.deduct.type]p1 :
问题的关键在于“兼容”在这里意味着什么 .
当部分订购功能模板时,Clang只是在两个方向上推断;如果演绎在一个方向而不是另一个方向上成功,则假定这意味着结果将是“兼容的”,并将其用作排序结果 .
然而,当部分排序类模板部分特化时,Clang将“兼容”解释为“相同” . 因此,如果将推导出的参数中的一个替换为另一个,则只考虑一个部分特化比另一个更专业化将再现原始的部分特化 .
更改这两个中的任何一个以匹配另一个会破坏大量的实际代码 . :(