使用未扩展的参数包作为模板模板参数的非类型模板参数的类型是否合法?

col*_*tam 5 c++ language-lawyer variadic-templates template-argument-deduction c++17

gcc 和 clang 对于以下代码是否应该编译存在分歧:

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

template <typename>
struct Test;

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

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

Test<tuple<O1<1>, O2<2>>> test;
Run Code Online (Sandbox Code Playgroud)

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

gcc 因推导失败而拒绝该代码。有趣的是,它确实接受 ifO2更改为采用long非类型模板参数,这对我来说似乎不一致。它表明类型可以扩展 ifTypes = {long, long}但不能扩展 if Types = {long, unsigned}

但是,从标准中我不清楚哪个编译器是正确的。核心问题是:在 表示的行上XXX,将参数包作为模板模板参数的非类型模板参数的类型是否有效?它应该像 clang 声称的那样扩展吗?

use*_*570 4

无效,因为:

类型参数包不能在其自己的参数子句中扩展。

从[temp.param]/17开始:

如果模板参数是在其可选标识符之前带有省略号的类型参数,或者是声明包 ([dcl.fct]) 的参数声明,则模板参数是模板参数包。模板参数包是一种参数声明,其类型包含一个或多个未扩展的包,该模板参数包是包扩展。...作为包扩展的模板参数包不得扩展在同一template-parameter-list中声明的模板参数包。

因此,请考虑以下无效示例:

template<typename... Ts, Ts... vals> struct mytuple {}; //invalid
Run Code Online (Sandbox Code Playgroud)

上面的例子是无效的,因为模板类型参数包Ts不能在自己的参数列表中扩展。

出于同样的原因,您的代码示例无效。