首先是一些代码,然后是一些上下文,然后是问题:
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
报告错误(这在实践中更有用)。至于铿锵,我真的不能说。
在实践中,似乎除了使用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测试,在这种情况下,我必须直接使用别名,因为不应该实例化结构。只是为了得到一个想法,实际代码的一部分是here。
答案 0 :(得分: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看到的内容。