17

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

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(零)。

id有一段template <typename>时间的事实,apply1首先apply2template <typename...>我感到担忧。然而,这个例子很方便,因为否则像apply1,这样的元函数apply2将不得不涉及更多。

另一方面,这样的模板别名会导致我无法在此处重现的实际代码中的严重问题: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 以各种方式崩溃而不是报告错误。有趣的是,虽然apply1,apply2似乎是等价的,但仅报告错误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;

0这在 clang 3.3、gcc 4.8.1、gcc 4.9.0 上都可以很好地编译和打印。

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

4

1 回答 1

3

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 所看到的。

于 2013-11-29T00:24:18.677 回答