C++11变长模板

C++11变长模板

简介

该篇博客主要介绍C++11中的变长模板,对变长模板的原理和使用方法进行介绍。本篇博客参考书籍深入理解C++11新特性解析与应用一书,非常推荐该书作为C++11学习的参考资料,英语好的话更推荐直接阅读C++官网中的关于C++11新特性的介绍。
该本书我已经上传高清pdf版本,并且包含非常详细的书签,下载地址如下(为上传修改了名称,放心下载):
C++11新特性解析与应用

模板和函数参数包

模板参数包说明

变长类模板的形式如下:

template <typename... Elements> class tuple;

标识符Elements之前的使用了省略号来表示该参数是变长的。在C++11中,Elements就被称作是一个模板参数包(template parameter pack),这是一种新的模板参数类型。它可以接受任意多个参数作为模板参数,实例化的tuple模板类如下所示:

tuple<int, char, double>

与普通的模板参数类似,模板参数包也可以是非类型的,例如

template<int... A> class NonTypeVT { };
NonTypeVT<1,0,2> vtvt;

除了类型的模板参数包和非类型的模板参数包,模板参数包实际上还是模板类型的,后面讨论。

解包

为了使用模板参数包,我们需要将其解包(unpack)。在C++11中,通过**包拓展(pack expansion)**的表达式来完成。例如:

template<typename... A> class Template: private B<A...> { };

这里的表达式A...就是一个包拓展。参数包会在包拓展的位置展开为多个参数。比如:

template<typename T1, typename T2> class B {};
template<typename... A> class Template: private B<A...> { };
Template<X, Y> xy;

上面的列子中展示了包拓展的实现,但是该例子基于类模板B总是接受两个参数的前提下。若我们在这里声明了一个Template<X, Y, Z>,就必然会发生模板推导的错误。为了解决上述问题,见后面内容中提出的解决办法。

在可变参数模板中使用递归

通过定义递归的模板偏特化定义,我们可以使得参数包在实例化时能够层层展开,直到参数包中的参数逐渐耗尽或到达某个数量的边界为止。
类模板使用递归代码如下所示(模板参数包):

template<typename... Elements> class tuple; // 变长模板的声明

template<typename Head, typename... Tail>           // 递归的偏特化定义
class tuple<Head, Tail...> : private tuple<Tail...> {
	Head head;
}

template<> class tuple<> {};  // 边界条件

上述代码中偏特化版本的tuple包含了两个参数,一个是类型模板参数Head,另一个则是模板参数包Tailtuple<double, int, char, float>会引起基类的递归构造,这样的递归构造在tuple的参数包为0个的时候会结束。这是由于我们定义了边界条件,即编译器从tuple<>建造出tuple<float>


函数模板中使用地推代码如下所示(函数参数包):

// definition for 0 parameters -- terminating call
void show_list3() {}

// definition for 1 or more parameters
template<typename T, typename... Args>
void show_list3( T value, Args... args)
{
    std::cout << value << ", ";
    show_list3(args...); 
}

模板参数包与函数参数包的区别是在C++11中,标准要求函数参数包必须唯一,且是函数的最后一个参数,而模板参数包则没有这样的要求。

进阶

C++11标准定义了以下7种参数包可以展开的位置:

  • 表达式
  • 初始化列表
  • 基类描述列表
  • 类成员初始化列表
  • 模板参数列表
  • 通用属性列表
  • lambda函数的捕捉列表

不同的包拓展方式

template<typename... A> class T: private B<A>... { };  // (1)
template<typename... A> class T: private B<A...> { };  // (2)

上述两种在基类描述列表中解包后是不同的,对于同样的实例化T<X, Y>,解包后的形式如下:

class T<X, Y>: private B<X>, private<Y> { };  // (1)
class T<X, Y>: private B<X, Y> { };  // (2)

类似的状况也会发生在函数模板中。

template<class... A> int Vaargs(A... args) {
	int size = sizeof...(A);
}

在C++11中引入了新操作符sizeof...,它的作用是计算参数包中的参数个数。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值