gcc vs clang 在具有可变参数和相同类型的额外参数的部分专业化上的行为

gcc vs clang behavior on partial specialization with variadic arguments plus extra argument of same type

以下代码:

#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;
}

...无法使用 g++(版本 5.1.05.3.0)编译,原因是:

error: partial specialization is not more specialized than the primary template because it replaces multiple parameters with a pack expansion

...但使用 clang.

编译

是否允许声明这样的偏特化?

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

gcc 是正确的,代码格式错误,因为专业化实际上并没有更专业化。


来自 [temp.class.spec] 的规则是(作为 DR 1495、h/t T.C 的结果。对于 link):

Within the argument list of a class template partial specialization, the following restrictions apply: [...] The specialization shall be more specialized than the primary template (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

然后通过部分排序规则。反过来,这涉及为每个模板参数合成新的 types/values 并查看推导是否可以在任一方向上成功。

当然,主专业的推导失败了(由于N vs 0)。在另一个方向,从 [temp.deduct.partial]:

If A was transformed from a function parameter pack and P is not a parameter pack, type deduction fails.

由于我们试图针对一个包推导 T first,因此推导在这个方向上也失败了。这意味着合成函数模板都不比其他模板更特化,这反过来意味着 class 模板特化并不比主模板更特化。因此,gcc 拒绝是正确的。

考虑:

template <class T, T first, T... rest>
struct X{};

template<class T, T... rest>
struct X<T, rest...>{};

完全相同的错误。 由于 first 可以解释为 rest 包的第一个元素,因此在这种专业化方面没有区别。如果您在专业化中添加 first - 那么,这是主要模板。在你的专业中,你可以添加 first:

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