C++ 11的可变模版参数是其新增的最强大的特性之一。通过对参数进行了泛化,可以表示从0到任意个数、任意类型的参数。我们知道对于一个模板类来说,通常只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改进。下面来具体说一下:

可变参数模板与通常的类模板写法相似。声明可变参数模板时需要在typename后面带上省略号 ... 。写法如下:

template<typename T, typename ...Types>
T mul(T head, Types ...rest){return head * mul<T>(rest...);
}

省略号当放在声明Types左边,表示一个参数包声明,这个参数包声明可以包括0以上任意多个参数;放在右边,如rest...则表示实际参数包,可以将其展开成一个一个独立的参数。我们无法直接获取参数包types中的每个具体参数的,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数。

首先来看一个最简单的可变模板参数:

#include <iostream>
#include <string>
#include <queue>
#include <vector>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <functional>
using namespace std;template <class... Types>
void f(Types... args)
{cout << sizeof...(args) << endl; //输出可变参数的总数量
}int main()
{f();f("aaa");f(1, 2.0);system("pause");return 0;
}

显然输出为0,1,2.  因为主函数中,f()没有传入参数,所以参数包为空,输出的size为0,后面两次调用分别传入1个和2个参数,故输出的size分别为1和2。由于可变模版参数的类型和个数是不固定的,所以我们可以传任意类型和个数的参数给函数f。这个例子只是简单的将可变模版参数的个数打印出来,如果我们需要将参数包中的每个参数打印出来的话就需要通过一些方法了。展开可变模版参数函数的方法一般有两种:一种是通过递归函数来展开参数包,另外一种是通过逗号表达式来展开参数包。下面来看看如何用这两种方法来展开参数包。

第一. 首先是通过递归函数的方法。

这种方法要求必须有明确的递归结束函数。比如下面的函数:

void print(){//递归结束函数cout << "this is the end!" << endl;
}template<typename T, typename ...args>
void print(T t, args ... arg){//每次递归,都调用头部参数进行操作cout << "this is arg: " << t << endl;print(arg...);
}int main()
{print(1, 2, 3, 4, 5);system("pause");return 0;
}
/*
输出是:
this is arg: 1
this is arg: 2
this is arg: 3
this is arg: 4
this is arg: 5
this is the end!
*/

我们观察上述的过程,每次调用模板类中的print(args...)函数,都会取其中头部的参数,剩下的参数则进行下一次的递归。也就是首先print(1,2,3,4,5)这个函数会对头部参数1进行操作,之后递归调用print(2,3,4,5)...最后调用到print();这个函数是递归结束函数。也就是展开参数包的函数有两个,一个是递归函数,另外一个是递归终止函数,参数包arg...在展开的过程中递归调用自己,每调用一次参数包中的参数就会少一个,直到所有的参数都展开为止,当没有参数时,则调用非模板函数print终止递归过程。

所以可变参数模板还可以模拟一个递归的过程,我们看下面的函数:

template <typename T>
T mul(T t){//递归终止函数,当递归的参数只有1个的时候返回结果并终止return t;
}
//展开函数
template<typename T, typename ...Types>
T mul(T head, Types ...rest){return head * mul<T>(rest...);
}int main()
{cout << mul<int>(1, 2, 3, 4, 5) << endl;system("pause");return 0;
}/*输出结果是120*/

展开相乘函数mul的时候,使用递归的形式,分别用1*mul(2,3,4,5) -> 1 * 2 * mul(3,4,5)->...->1*2*3*4*mul(5) = 120;

第二种方式是逗号表达式

用递归的办法很好理解,但是他要求必须有明确的递归结束函数。那么有没有办法不用写递归结束的函数呢?这就用到逗号表达式的解决办法了。我们看下面的一个例子:

template<typename T>
void printarg(T t)
{//do nothing
}template <class ...Args>
void expand(Args... args)
{int arr[] = { (printarg(args), 0)... };for (int i = 0; i < sizeof(arr)/4; i++){cout << arr[i] << " ";}cout << endl;
}int main()
{expand(1, 2, 3, 4, 5);system("pause");return 0;
}

上面这个函数中的:

int arr[] = { (printarg(args), 0)... };

这句话实际上起到的是初始化一个长度为args参数数量的int形数组。对于(printarg(args), 0)... 作为可变参数模板,可以展开写成:

(printarg(args[0]), 0),(printarg(args[1]), 0),(printarg(args[2]), 0) ... (printarg(args[n]), 0).而对于逗号表达式,c = (a,b) 就是按照顺序执行c = b,所以最终上面式子的结果就是0,0,0,0...0。再加上外层的大括号作为初始化,也就实际上展开为:

int arr[] = {0,0,...0}一共n个0了。在创建数组的过程中会先执行逗号表达式前面的部分printarg(args)打印出参数(虽然这个函数什么也没做),也就是说在构造int数组的过程中就将参数包展开了,这个数组的目的纯粹是为了在数组构造的过程展示参数包的过程。

C++11新特性之 可变参数模板相关推荐

  1. Java1.5增加了新特性:可变参数

    /* Java 可变参数 Java1.5增加了新特性:可变参数:适用于参数个数不确定,类型确定的情况,java把可变参数当做数组处理. 注意:可变参数必须位于最后一项.当可变参数个数多余一个时,必将有 ...

  2. jdk1.5之后的新特性之可变参数

    Java1.5增加了新特性:可变参数:适用于参数个数不确定,类型确定的情况,java把可变参数当做数组处理. 注意:可变参数必须位于最后一项.当可变参数个数多余一个时,必将有一个不是最后一项,所以只支 ...

  3. JDK5新特性系列:可变参数

      在进入正题之前我们先来看一个简单的案例,它的需求是这样的:我们有一个Shoes类,在该类中有鞋子的名字name,鞋子的价钱price,以及要购买的鞋子的数量num. @SuppressWarnin ...

  4. C++11新特性之泛型编程与模板

    模板 泛型编程 函数模板 普通函数模板 成员函数模板 函数模板重载 模板函数的特化 类模板 类模板中的成员函数模板 类模板的特化与偏特化 类模板成员特化 模板 Template所代表的泛型编程是C++ ...

  5. 圣思园java se培训总结(58-)(java1.5新特性,可变参数,包装类)

    Integer 会有一个数组缓存,缓存-128到127之间 可变参数,必须是方法声明中的最后一个参数!可变参数本质是一个数组,传递参数时可以传离散的数,也可以传一个数组!当然一个方法不能有两个或者两个 ...

  6. JDK5 新特性之 可变参数的方法(2)---asList

    > Arrays.asList(T - a)方法的使用 >UnsupportedOperationException分析 Arrays.asList(T - a)方法的使用 package ...

  7. c++11新特性的使用---可变模板参数、type_traits、function综合使用

    DLL帮助类 c++中手动调用dll是比较繁琐的,调用过程是:加载dll后还要定义一个对应的函数指针类型,接着调用GetProAddress获取函数地址,在转成函数指针,最后调用该函数如下例子: #i ...

  8. [C++11新特性](24)列表初始化,右值引用,可变参数模板,lambda表达式,包装器

    文章目录 列表初始化 {}初始化 initializer_list auto.nullptr.范围for decltype STL的变化 右值引用 简介 移动构造与移动赋值 完美转发 新的类功能 可变 ...

  9. C++11新特性选讲 语言部分 侯捷

    C++11新特性选讲 语言部分 侯捷 本课程分为两个部分:语言的部分和标准库的部分.只谈新特性,并且是选讲. 本文为语言部分笔记. 语言 Variadic Templates move semanti ...

最新文章

  1. 调研字节码插桩技术,用于系统监控设计和实现
  2. 上海市国资大数据课题启动仪式暨数据资产技术及金融行业应用沙龙隆重开幕...
  3. 23个 Git 最常用命令速查手册,值得收藏!
  4. 万字干货:教新手从0到1搭建完整的增长数据体系(30图)
  5. 数据库原理与应用(SQL Server)笔记 第五章 索引和视图
  6. [SNOI2017]遗失的答案 (FWT)
  7. C#学习日志三(流程控制语句)
  8. Nodejs学习笔记(四)——http协议与服务器
  9. nodpad 设置护眼_Notepad++更改背景颜色(护眼色)
  10. rpm方式安装MySQL-5.6
  11. 溢出检测单符号位法_设计经验:如何用三轴加速度传感器检测倾斜角?
  12. 输出当前MySQL的环境变量:
  13. iptables详解 1 -- iptables概念
  14. 解决360N4S骁龙版在国外使用碰到的问题,附详细root教程
  15. TeamTalk 线程池详解
  16. leapftp,leapftp怎么上传文件
  17. 子域名收集 -- 提莫(teemo)
  18. Latex 字母上面加符号 波浪线 横线 角号等
  19. 电大计算机应用基础word排版,电大计算机应用基础考试全部操作100题
  20. android 字体跑马灯,Android中使用TextView实现文字跑马灯效果

热门文章

  1. python中文意思-请问在python中**是啥什么意思?
  2. 30岁学python有前途吗-30岁新手入门python!尝试人生另一种可能
  3. 语音识别实验报告.docx
  4. 用matlab仿真0到9十个数字的语音识别
  5. 远场(far-field)语音识别的主流技术有哪些?
  6. start.bat怎么启动java项目_部署java项目为服务,设置开机自启动
  7. linux向脚本传递参数,Linux 使用位置变量向脚本传递参数
  8. 【深度优先搜索笔记】抽象DFS
  9. 【python笔记】:python面向对象实现学生管理系统
  10. matlab仿真弹簧波,基于Matlab/Simulink的三弹簧谐振子微振动的仿真实验