别名模板的包扩展

Posted

技术标签:

【中文标题】别名模板的包扩展【英文标题】:Pack expansion for alias template 【发布时间】:2015-08-22 19:02:44 【问题描述】:

似乎只能扩展包参数来代替别名模板的包参数。这不适用于类或函数模板:

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

更简单的情况:

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

template <class... Args> auto f(Args...) -> void 
  y_t<Args...> v4; // Error

上述代码在g++ 4.9g++ 5.1clang 3.5 中的c++11c++14 都会产生错误(即使f 从未实例化)。

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

至于为什么不用第一个变体写成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’

clang 错误:

error: pack expansion used as argument for non-pack parameter of
alias template   x_t<Args...> v2;

【问题讨论】:

这里的信息是相关的,我认为:***.com/q/24433658/4326278 另外,对于它的价值,MSVC 12 和 14 RC 编译这个没有诊断(除了未引用变量的正常警告) - 正如他们所说的“实现差异”。 在 g++ 4.8.2 中工作正常。 ICC 13 也接受该代码——有没有标准的人可以指出说不?否则,这是一个很好的发现——回归不是一个,而是 两个 不同的实现! 【参考方案1】:

这在 GCC 4.8 中编译,但在 GCC 4.9 中失败,这证明它与 CWG 1430 和 bug report #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;

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

实际上,不,这在很大程度上是 Core 1430 的问题;没有办法 mangle variadic_alias 不提及别名的名称 修饰中的模板,它们应该完全是 透明。这只是偶然在 4.8 中有效,因为检查是 为发布禁用。

错误报告中没有进一步的讨论,因此我们可以求助于 CWG 1430:

最初,包扩展无法扩展为固定长度 模板参数列表,但在 N2555 中已更改。这有效 大多数模板都可以,但会导致别名模板出现问题。

在大多数情况下,别名模板是透明的;当它用于 模板我们可以只替换依赖模板参数。 但是,如果模板 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...>);

没有办法用S来表示A,所以我们需要持有 到 A 上,直到我们有 Ts 可以替换,因此它 需要在 mangling 中处理。

目前,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>();
  

这个例子的处理存在实现差异。

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

【讨论】:

以上是关于别名模板的包扩展的主要内容,如果未能解决你的问题,请参考以下文章

嵌套模板的模板模板别名?

如何编写聚合模板别名的推导指南?

git别名可以像bash别名一样扩展吗?

[C++11]使用using和typedef给模板定义别名

模板别名、变量模板和自动类型推导无法推导模板参数

C++开发之using定义模板别名