Bar*_*rry 43 c++ templates partial-ordering language-lawyer
选择哪个类模板特化的首选规则包括将特化重写为函数模板,并通过函数模板[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>{});
}
Run Code Online (Sandbox Code Playgroud)
gcc和clang都打印2
在这里.这是有道理的一些前面的例子-推导对非推测的情况下(void
对void_t<T>
)只是忽略,所以推断<T, void_t<T>>
反对<X*, void>
成功,但推断<T*, void>
针对<Y, void_t<Y>>
在两个参数失败.精细.
现在考虑这种概括:
#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';
}
Run Code Online (Sandbox Code Playgroud)
clang和gcc都认为这种专业化是模糊的,介于1
和之间2
.但为什么?合成的函数模板不是模糊的.这两种情况有什么区别?
Clang与GCC兼容(并且与依赖于这两种行为的现有代码兼容).
考虑[temp.deduct.type] p1:
[...]尝试在替换推导值后,查找将生成P的模板参数值(类型参数的类型,非类型参数的值或模板参数的模板) (称之为推导出的A),与A兼容.
问题的关键在于"兼容"在这里意味着什么.
当部分订购功能模板时,Clang只是在两个方向上推断; 如果扣除在一个方向而不是另一个方向上成功,则假定这意味着结果将是"兼容的",并将其用作排序结果.
然而,当部分排序类模板部分特化时,Clang将"兼容"解释为"相同".因此,它只考虑一个部分特化比另一个更专业化,如果将推导出的参数从其中一个替换为另一个将再现原始的部分特化.
改变这两者中的任何一个以匹配另一个会破坏大量的实际代码.:(