首先是一些代码,然后是一些上下文,然后是问题:
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
首先apply2
让template <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 测试,在这种情况下,我必须直接使用别名,因为不应该实例化结构。只是想了解一下,这里有一段实际代码。