【问题标题】:Variadic template aliases as template arguments可变模板别名作为模板参数
【发布时间】:2013-11-27 21:32:02
【问题描述】:

首先是一些代码,然后是一些上下文,然后是问题:

template <typename T> using id = T;

template <template <typename...> class F, typename... T>
using apply1 = F <T...>;

template <template <typename...> class F>
struct apply2
{
    template <typename... T>
    using map = F <T...>;
};

// ...

cout << apply1 <id, int>() << endl;
cout << apply2 <id>::map <int>() << endl;

clang 3.3 和 gcc 4.8.1 均无错误地编译它,将标识元函数应用于 int,因此两个表达式的计算结果均为默认 int(零)。

idtemplate &lt;typename&gt;apply1apply2 期望 template &lt;typename...&gt; 的事实首先让我担心。然而,这个例子很方便,因为否则像apply1apply2 这样的元函数将不得不更多地参与。

另一方面,此类模板别名会导致我无法在此处重现的实际代码中的严重问题:gcc 的内部编译器错误频繁,clang 的意外行为较少(仅在更高级的 SFINAE 测试中)。

经过几个月的反复试验,我现在在(实验性)gcc 4.9.0 上安装并尝试了代码,但出现了错误:

test.cpp: In instantiation of ‘struct apply2<id>’:
test.cpp:17:22: error: pack expansion argument for non-pack parameter ‘T’ of alias template ‘template<class T> using id = T’
  using map = F <T...>; 
                      ^

好的,所以这段代码似乎一直无效,但是 gcc 以各种方式崩溃而不是报告错误。有趣的是,虽然apply1apply2 看起来是等价的,但错误只报告给apply2(这在实践中更有用)。至于clang,我真的不能说。

实际上,我似乎别无他法,只能使用 gcc 4.9.0 并更正代码,尽管它会变得更加复杂。

理论上,我想知道标准是怎么说的:这段代码有效吗?如果不是,apply1 的使用是否也无效?还是只有apply2

编辑

只是为了澄清到目前为止我遇到的所有问题都是指模板别名,而不是模板结构。例如,考虑以下修改:

template <typename T> struct id1 { using type = T; };

// ...

cout << typename apply1 <id1, int>::type() << endl;
cout << typename apply2 <id1>::map <int>::type() << endl;

在clang 3.3、gcc 4.8.1、gcc 4.9.0上,这两种情况下都可以正常编译并打印0

在大多数情况下,我的解决方法是在别名之前引入一个中间模板结构。但是,我现在尝试使用元函数来参数化通用 SFINAE 测试,在这种情况下,我必须直接使用别名,因为不应该实例化结构。只是想知道,一段实际代码是here

【问题讨论】:

  • 实验性GCC 4.9的错误信息对我来说没有意义,FWIW我认为代码是有效的。
  • 谢谢,这个问题与处理 foofoo2foo_variadic 等特殊情况的模板有关.但是,正如我在上面编辑的那样,我的问题只出现在模板别名中。
  • FWIW,clang++ 3.6.0和g++ 5.1.0都报错。

标签: c++ c++11 variadic-templates template-meta-programming template-aliases


【解决方案1】:

ISO C++11 14.3.3/1:

模板模板参数的模板参数应该是类模板或别名模板的名称,表示为id-expression。

另外,我没有看到可变参数模板模板参数有任何特殊例外。

另一方面,此类模板别名会导致我无法在此处重现的实际代码中的严重问题:gcc 的内部编译器错误频繁,clang 的意外行为频率较低(仅在更高级的 SFINAE 测试中)。

问题的根源可能在其他地方。您应该尝试本地化导致内部编译器错误的代码 - 只需逐个删除不相关的部分(或使用某种二进制搜索,即分而治之) - 并检查每个阶段是否仍然存在错误。


至于GCC 4.9.0报错,换个试试

template <typename... T>
using map = F <T...>;

template <typename... U>
using map = F <U...>;

也许这有助于理解 GCC 所看到的。

【讨论】:

  • 谢谢。过去我无法隔离出错的代码,因为即使删除很小的部分也会使错误消失。使用 gcc 4.9,这是我第一次看到明确的错误消息,我很“高兴”,因为如果属实,它几乎可以解释所有事情(即使每个解决方案都很难看)。无论如何,由于问题仍然存在,我将尝试在其他编译器中创建一个具有意外结果的新示例,然后提出一个新问题。
猜你喜欢
  • 2016-11-18
  • 2013-12-15
  • 1970-01-01
  • 2017-09-22
  • 1970-01-01
相关资源
最近更新 更多