具有可变参数的部分特化的gcc vs clang行为加上相同类型的额外参数

Hol*_*olt 8 c++ template-specialization variadic-templates c++11

以下代码:

#include <cstddef>

template <size_t N,
          typename T,
          T first,
          T... rest>
struct A {
};

template<typename T,
         T... args>
struct A<0, T, args...> {
};

int main () {
    A<0, int, 1> a0;
    A<2, int, 1, 2> a2;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

... 由于以下原因而无法编译g++(版本5.1.05.3.0):

错误:部分特化并不比主模板更专业,因为它用包扩展替换了多个参数

...但是要编译clang.

是否允许宣布这种部分专业化?

旁注:实际上,专业化是危险的,因为A<0, int>无法使用两个编译器进行编译(模板参数数量错误).

Bar*_*rry 8

gcc是正确的,代码格式不正确,因为专业化实际上并不是更专业化.


来自[temp.class.spec]的规则是(作为DR 1495的结果,链接的h/t TC):

在类模板部分特化的参数列表中,以下限制适用:[...]专业化应比主模板(14.5.5.2)更专业化.

为了确定这一点,我们将两个重写为合成函数模板:

template <size_t N, class T, T first, T... rest>
void __f(A<N, T, first, rest...> );  // primary

template <class T, T... args>
void __f(A<0, T, args...> );         // specialization
Run Code Online (Sandbox Code Playgroud)

然后完成部分排序规则.反过来,这涉及为每个模板参数合成新的类型/值,并查看演绎是否可以在任一方向上成功.

当然,专业化的推断因主要而失败(由于Nvs 0).在另一个方向,来自[temp.deduct.partial]:

如果A从函数参数包转换而P不是参数包,则类型推导失败.

因为我们试图推断T first一个包,所以扣除也会在这个方向上失败.这意味着合成的函数模板都不比其他模板更专业,这反过来意味着类模板特化不比主模板更专业化.因此,gcc拒绝是正确的.