可变参数模板别名作为模板参数

时间:2013-11-27 21:32:02

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

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

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>apply1apply2期待template <typename...>首先关注我。但是,这个示例很方便,因为否则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报告错误(这在实践中更有用)。至于铿锵,我真的不能说。

在实践中,似乎除了使用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

1 个答案:

答案 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看到的内容。