您可以使用单独模板参数列表中的未扩展参数包作为模板模板参数的非类型模板参数的类型吗?

col*_*tam 6 c++ language-lawyer variadic-templates non-type-template-parameter

在我之前的问题(使用未展开的参数包作为模板模板参数的非类型模板参数的类型是否合法?)中,有人指出在同一个模板参数列表中展开参数包是不合法的它是在 [temp.param]/17 中声明的。然而,当扩展在单独的参数列表中完成时,问题仍然存在。在下面的代码中:

template <typename... Args>
struct tuple {};

template <typename... Types>
struct A {
    template <typename>
    struct B;
};

template <typename... Types>
template <
    template <Types> typename... Outer, // XXX
    Types... Inner
>
struct A<Types...>::B<tuple<Outer<Inner>...>> {};

template <long T> struct O1 {};
template <unsigned T> struct O2 {};

A<long, unsigned>::B<tuple<O1<1>, O2<2>>> test;
Run Code Online (Sandbox Code Playgroud)

clang 接受代码,推导出 Types = {long, unsigned}、Outer = {O1, O2}、Inner={1L, 2U}。从结构上看,这似乎是正确的。

gcc 对代码进行 ICE,而 MSVC 和 ICC 拒绝它。

该代码有效吗?在 表示的行上XXX,将参数包作为模板模板参数的非类型模板参数的类型是否有效?

小智 1

在我看来,你的代码有效。

请参考标准。第 370 页,(5.3.1)。

XXX 行上的代码是包扩展模式,即参数声明。

另外,Types这里不应将其视为参数包本身,它是扩展模式的内部部分,因此不存在“参数包作为类型”。

另外,不应违反上一个问题的模板子句中有关扩展包的规则,因为您有两个不同的模板,当编译器扩展内部模板时,必须Types已经从外部模板推断出来。

不管怎样,对我来说,这很奇怪,gcc ICEs 和 msvc 拒绝这个代码。