别名模板的包扩展

bol*_*lov 40 c++ language-lawyer variadic-templates c++11 template-aliases

似乎只能在别名模板的pack参数的位置扩展pack参数.对于类或函数模板,情况并非如此:

template <class T, class... Args> struct x { using type = T; };

template <class T, class... Args> using x_t     = typename x<T, Args...>::type;
template <class... Args>          using x_fix_t = typename x<Args...>::type;

template <class... Args> auto f(Args...) -> void {
  typename x<Args...>::type v1; // OK
  x_t<Args...> v2; // Error
  x_fix_t<Args...> v3; // OK
}
Run Code Online (Sandbox Code Playgroud)

更简单的情况:

template <class T, class U> using y_t = T;

template <class... Args> auto f(Args...) -> void {
  y_t<Args...> v4; // Error
}
Run Code Online (Sandbox Code Playgroud)

上面的代码生成错误(即使f从未实例化)与both c++11c++14in g++ 4.9,g++ 5.1clang 3.5.

为什么不允许这样做,一般规则是什么?我认为没有理由限制这一点.这似乎是一个非常奇怪的禁令.

至于为什么不像x_fix_t第一个变体那样写作,更明确的是x_t有一个强制性的第一个参数.(例如,这是f()不允许的原因).但这并不重要,修复很容易.问题仍然存在:为什么?

gcc错误:

error: pack expansion argument for non-pack parameter ‘T’ of
alias template ‘template<class T, class ... Args> using x_t = typename x::type’
Run Code Online (Sandbox Code Playgroud)

铿锵声:

error: pack expansion used as argument for non-pack parameter of
alias template   x_t<Args...> v2;
Run Code Online (Sandbox Code Playgroud)

小智 19

这在GCC 4.8中编译但在GCC 4.9中失败,这证明它与DR1430和错误报告#59498有关.Roy Chrihfield提出的解决方案与您完全相同:

Rewriting the code to use a struct succeeds:

template <typename T, typename ...>
struct alias { using type = T; };

template <typename ...T>
using variadic_alias = typename alias<T...>::type;
Run Code Online (Sandbox Code Playgroud)

此外,Jason Merrill详细阐述了它为什么会失败:

实际上,不,这是一个核心1430问题; 没有提到修改过程中别名模板的名称就没有办法破坏variadic_alias,它们应该是完全透明的.这仅适用于4.8,因为发布时禁用了检查.

错误报告中不再存在进一步的讨论,因此我们可以转向DR1430:

最初,包扩展无法扩展到固定长度的模板参数列表,但在N2555中已更改.这适用于大多数模板,但会导致别名模板出现问题.

在大多数情况下,别名模板是透明的; 当它在模板中使用时,我们可以在依赖模板参数中替换.但是,如果template-id对非可变参数使用包扩展,则这不起作用.例如:

template<class T, class U, class V>
struct S {};

template<class T, class V>
using A = S<T, int, V>;

template<class... Ts>
void foo(A<Ts...>);
Run Code Online (Sandbox Code Playgroud)

没有办法用S来表达A,所以我们需要保持A直到我们有Ts代替,因此它需要在修改中处理.

目前,EDG和Clang拒绝这个测试用例,抱怨A.G ++的模板参数太少,但我认为这是一个错误.然而,在ABI名单上,John Spicer认为它应该被拒绝.

(另见问题1558.)

2012年10月会议记录:

CWG的共识是,禁止使用此用法,当依赖参数不能简单地直接替换为type-id时,禁止使用别名模板.

附加说明,2013年4月:

再举一个例子,考虑:

  template<class... x> class list{};
  template<class a, class... b> using tail=list<b...>;
  template <class...T> void f(tail<T...>);

  int main() {
    f<int,int>({});
  }
Run Code Online (Sandbox Code Playgroud)

处理此示例时存在实现差异.

换句话说,这是一个持续的问题,没有任何解决方案(AFAIC).