47

似乎一个包参数只能在别名模板的包参数的位置展开。这不适用于类或函数模板:

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
}

上面的代码使用and in和生成错误(即使f从未实例化)。c++11c++14g++ 4.9g++ 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’

叮当错误:

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

1 回答 1

28

这在 GCC 4.8 中编译,但在 GCC 4.9 中失败,这证明它与 CWG 1430 和错误报告 #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 的问题;没有在修改中提及别名模板的名称就无法修改 variadic_alias<T...> ,并且它们应该是完全透明的。这仅在 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<Ts...>,所以我们需要保留 A 直到我们有 Ts 可以替换,因此需要在 mangling 中处理它。

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

(另见问题 1558。)

2012 年 10 月会议记录:

CWG 的共识是应该禁止这种用法,当不能简单地将依赖参数直接替换为类型 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)。

于 2015-10-14T16:40:24.650 回答