42

选择哪个类模板特化是首选的规则包括将特化重写为函数模板,并通过函数模板的排序规则 [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><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。但为什么?合成的函数模板没有歧义。这两种情况有什么区别?

4

1 回答 1

6

Clang 与 GCC 兼容(并且与依赖于这两种行为的现有代码兼容)。

考虑[temp.deduct.type]p1

[...] 尝试查找模板参数值(类型参数的类型、非类型参数的值或模板参数的模板),在替换推导的值后,将生成 P (称其为推导的 A),与 A 兼容。

问题的症结在于这里的“兼容”是什么意思。

在对函数模板进行部分排序时,Clang 只是双向推导;如果推论在一个方向上成功但在另一个方向上不成功,则假设这意味着结果将是“兼容的”,并将其用作排序结果。

然而,当对类模板部分特化进行部分排序时,Clang 将“兼容”解释为“相同”的意思。因此,如果将其中一个推导的论点代入另一个会重现原始的部分专业化,它只会认为一个部分专业化比另一个更专业化。

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

于 2018-02-17T01:36:36.220 回答