选择哪个类模板特化是首选的规则涉及将特化重写为函数模板,并通过函数模板[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>
)进行推断只会被忽略,因此针对<T, void_t<T>>
推导<X*, void>
会成功,但会推导{{ 1}}对<T*, void>
两个参数都失败了。细
现在考虑这种概括:
<Y, void_t<Y>>
clang和gcc都认为此专业化在#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';
}
和1
之间不明确。但为什么?合成的函数模板不是模糊的。这两种情况有什么区别?
答案 0 :(得分:5)
Clang与GCC兼容(并且与依赖于这两种行为的现有代码兼容)。
考虑 [temp.deduct.type] p1 :
[...]尝试在替换之后找到将生成P的模板参数值(类型参数的类型,非类型参数的值或模板参数的模板)推导出的值(称之为推导出的A),与A兼容。
问题的关键在于&#34;兼容&#34;在这里意味着。
当部分订购功能模板时,Clang只是在两个方向上推断;如果扣除在一个方向而不是另一个方向上成功,则假定结果将是&#34;兼容&#34;,并将其用作排序结果。
当部分排序类模板部分特化时,Clang解释&#34;兼容&#34;作为意义&#34;相同&#34;。因此,如果将推导出的参数从其中一个替换为另一个会重现原始的部分特化,它只会认为一个部分特化比另一个更专业化。
更改这两个中的任何一个以匹配另一个会破坏大量的实际代码。 :(