Class 模板特化偏序和函数综合
Class template specialization partial ordering and function synthesis
选择 class 模板特化首选的规则涉及将特化重写到函数模板中,并通过函数模板的排序规则 [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>>
成功但推导 <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';
}
clang 和 gcc 都将此专业化报告为不明确,介于 1
和 2
之间。但为什么?合成函数模板没有歧义。这两种情况有什么区别?
Clang GCC-compatible(并与依赖于这两种行为的现有代码兼容)。
考虑 [temp.deduct.type]p1:
[...] an attempt is made to find template argument values (a type for a type parameter, a value for a non-type parameter, or a template for a template parameter) that will make P, after substitution of the deduced values (call it the deduced A), compatible with A.
问题的症结在于"compatible"在这里的意思。
当对函数模板进行部分排序时,Clang 仅在两个方向进行推导;如果推导在一个方向上成功但在另一个方向上不成功,它假定这意味着结果将是 "compatible",并将其用作排序结果。
然而,当对 class 模板偏特化进行部分排序时,Clang 将 "compatible" 解释为 "the same"。因此,如果将其中一个的推导参数替换为另一个会再现原始的部分专业化,它只会认为一个部分专业化比另一个更专业化。
更改这两个中的任何一个以匹配另一个会破坏大量的实际代码。 :(
选择 class 模板特化首选的规则涉及将特化重写到函数模板中,并通过函数模板的排序规则 [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>>
成功但推导 <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';
}
clang 和 gcc 都将此专业化报告为不明确,介于 1
和 2
之间。但为什么?合成函数模板没有歧义。这两种情况有什么区别?
Clang GCC-compatible(并与依赖于这两种行为的现有代码兼容)。
考虑 [temp.deduct.type]p1:
[...] an attempt is made to find template argument values (a type for a type parameter, a value for a non-type parameter, or a template for a template parameter) that will make P, after substitution of the deduced values (call it the deduced A), compatible with A.
问题的症结在于"compatible"在这里的意思。
当对函数模板进行部分排序时,Clang 仅在两个方向进行推导;如果推导在一个方向上成功但在另一个方向上不成功,它假定这意味着结果将是 "compatible",并将其用作排序结果。
然而,当对 class 模板偏特化进行部分排序时,Clang 将 "compatible" 解释为 "the same"。因此,如果将其中一个的推导参数替换为另一个会再现原始的部分专业化,它只会认为一个部分专业化比另一个更专业化。
更改这两个中的任何一个以匹配另一个会破坏大量的实际代码。 :(