首页 文章

类模板特化部分排序和函数合成

提问于
浏览
43

选择哪个类模板特化的首选规则包括将特化重写为函数模板,并通过函数模板[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 . 这对前面的一些例子很有意义 - 根据非演绎的上下文( voidvoid_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都报告这个专业化是不明确的,在 12 之间 . 但为什么?合成的函数模板不是这两种情况之间的区别吗?

1 回答

  • 5

    Clang is being GCC-compatible (并与依赖于这两种行为的现有代码兼容) .

    考虑 [temp.deduct.type]p1

    [...]尝试在替换推导出的P之后,找到模板参数值(类型参数的类型,非类型参数的值或模板参数的模板)值(称之为推导出的A),与A兼容 .

    问题的关键在于“兼容”在这里意味着什么 .

    当部分订购功能模板时,Clang只是在两个方向上推断;如果演绎在一个方向而不是另一个方向上成功,则假定这意味着结果将是“兼容的”,并将其用作排序结果 .

    然而,当部分排序类模板部分特化时,Clang将“兼容”解释为“相同” . 因此,如果将推导出的参数中的一个替换为另一个,则只考虑一个部分特化比另一个更专业化将再现原始的部分特化 .

    更改这两个中的任何一个以匹配另一个会破坏大量的实际代码 . :(

相关问题