1.关于

本文略带总结性,参考:泛化之美--C++11可变模版参数的妙用

参数包展开方式有两种: 递归展开 和 逗号表达式展开。

本文代码并非全部来自参考文章,自己做了注释和修改。请以原文为准

2. 查询参数包的参数个数

用下面的函数可以实现查询参数包的个数

  1. template<typename... Args>
  2. unsigned int length(Args... args)
  3. {
  4. return sizeof...(args);
  5. }

调用

  1. length(1, "hello", "c++", "11"); // 返回4,4个参数

3.递归展开

3.1 注意, 递归展开需要考虑爆栈的情况。 说到这里,ubuntu(linux) 默认栈大小8M(使用命令 ulimit -a查看), Win下,Visual Studio 默认栈大小1M(连接器->系统)。

3.2 需要两个函数:递归终止函数递归函数

3.3 一个例子,输出各个元素。函数参数为参数包的形式,使用递归展开.

  1. // 1.递归终止函数
  2. void print()
  3. {
  4. ;
  5. }
  6. // 2.递归执行函数
  7. template<typename T, typename... Args>
  8. void print(T head, Args... args)
  9. {
  10. std::cout << head << (sizeof...(args) > 0 ? ", " : "");
  11. print(args...);
  12. }

3.4 调用及执行结果

  1. print(1, "hello", "C++", 11);

3.5 其执行过程

  1. print(1, "hello", "C++", 11);
  2. print("hello", "C++", 11);
  3. print("C++", 11);
  4. print(11);
  5. print();

例子中,递归终止到第0参数截止。根据执行过程可知,终止函数可以改为1个参数,最后一个函数仅仅作为终止递归使用,终止递归改为1个参数的函数还可以提高性能(减少函数的调用)。

3.6 递归终止改为一个参数

  1. // 1.递归终止函数
  2. template<typename T>
  3. void print(const T& t)
  4. {
  5. std::cout << t << endl;
  6. }
  7. // 2.递归执行函数
  8. template<typename T, typename... Args>
  9. void print(T head, Args... args)
  10. {
  11. std::cout << head << (sizeof...(args) > 0 ? ", " : "");
  12. print(args...);
  13. }

同样的,执行上面的调用代码print(1, "hello", "C++", 11);,其执行过程如下

  1. print(1, "hello", "C++", 11);
  2. print("hello", "C++", 11);
  3. print("C++", 11);
  4. print(11);

3.7 再看一个递归求和的例子, 同样是使用参数包的形式

  1. // 1.递归终止函数
  2. template <typename T>
  3. T sum(const T& t)
  4. {
  5. return t;
  6. }
  7. // 2.递归函数
  8. template <typename T, typename ... Args>
  9. T sum(const T& head, const Args... args)
  10. {
  11. return head + sum<T>(args...);
  12. }

4.逗号表达式展开

  逗号表达式?了解一下

4.1 回到上面print的例子,改为逗号表达式

  1. // 1.用作输出每个参数
  2. template<typename T>
  3. void show_arg(const T& t)
  4. {
  5. std::cout << t <<std::endl;
  6. }
  7. // 2.用作展开参数包
  8. template <typename ... Args>
  9. void expand(const Args&... args)
  10. {
  11. int arr[] = {(show_arg(args), 0)...}; /// (1)
  12. }

代码中,(1)的逗号表达式为: (show_arg(args), 0)。先执行表达式1(show_arg(args)),再执行表达式2(0)。 使用调用代码expand(1, 2, 3, 4),(1)处的逗号表达式展开结果如下

  1. int arr[] = { (show_arg(1), 0) , (show_arg(2), 0), (show_arg(3), 0), (show_arg(4), 0) };

而,第一个逗号表达式结果为0, 第二逗号表达式的结果为0, .... 最后一个逗号表达式展开后,逗号表达式的结果为0,所以(1)的代码最后是这样的:

  1. int arr[] = {0, 0, 0, 0}; /// (2)

(2)的代码声明了一个int数组,数组有4个元素。 展开参数包的方式相当巧妙,比起递归,更加方便了。

4.2 使用lambda + 逗号表达式展开参数包

还是同样的例子,输出参数包元素。


  1. template<typename Func, typename...Args>
  2. void show_args(const Func &f, Args&... args)
  3. {
  4. // 初始化列表,列表的类型为int, std::initializer_list是c++11引入的模板
  5. std::initializer_list<int>{(f(std::forward< Args>(args)), 0)...};
  6. }
  7. // 调用
  8. show_args([](const int & item) {std:: cout << item << ", "; }, 1, 2, 3, 4); /// 输出: 1, 2, 3, 4,

c++11之函数参数包展开的更多相关文章

  1. C++11变长参数模板

    [C++11变长参数模板] C++03只有固定模板参数.C++11 加入新的表示法,允许任意个数.任意类别的模板参数,不必在定义时将参数的个数固定. 实参的个数也可以是 0,所以 tuple<& ...

  2. 泛化之美--C++11可变模版参数的妙用

    1概述 C++11的新特性--可变模版参数(variadic templates)是C++11新增的最强大的特性之一,它对参数进行了高度泛化,它能表示0到任意个数.任意类型的参数.相比C++98/03 ...

  3. -C++11可变模版参数(转载)

    泛化之美--C++11可变模版参数的妙用 1概述 C++11的新特性--可变模版参数(variadic templates)是C++11新增的最强大的特性之一,它对参数进行了高度泛化,它能表示0到任意 ...

  4. c++之可变参数格式化字符串(c++11可变模板参数)

    本文将使用 泛型 实现可变参数. 涉及到的关见函数:  std::snprintf 1.一个例子 函数声明及定义 1 // 泛型 2 template <typename... Args> ...

  5. [C++11]C++可变参数模板

    可变参数模板 原文链接: http://blog.csdn.net/xiaohu2022/article/details/69076281 普通模板只可以采取固定数量的模板参数.然而,有时候我们希望模 ...

  6. ES6之函数参数

    ES6中对于函数参数主要增加了以下内容: 1.参数的扩展/数组的展开: 2.默认参数. 什么是参数的扩展? 看下面代码: <!DOCTYPE html> <html lang=&qu ...

  7. 2、Golang基础--包的使用、if-else语句、循环、switch语句、数组、切片、可变函数参数、map类型

    1 包的使用 // 为了便于组织代码,同一种类型的代码,写在同一个包下,便于管理 // 定义包 -新建一个文件夹 -内部有很多go文件 -在每个go文件的第一行,都要声明包名,并且包名必须一致 -在一 ...

  8. 用C++11实现C++17的apply(动态数组用作函数参数)

    标题有点错误,apply是用tuple做参数,调用一个函数.这个标题是为了能更好的适配搜索关键字. 动态数组用作函数参数更适合嵌入了脚本环境的C++程序,比如lua或javascript(js). 若 ...

  9. Oracle中如何导出存储过程、函数、包和触发器的定义语句?如何导出表的结构?如何导出索引的创建语句?

    Oracle中如何导出存储过程.函数.包和触发器的定义语句?如何导出表的结构?如何导出索引的创建语句? QQ群里有人问:如何导出一个用户下的存储过程?   麦苗答:方法有多种,可以使用DBMS_MET ...

随机推荐

  1. ubuntu20.04安装EasyConnect兼容性问题解决

    目录 1. 命令行启动EasyConnect 2. 降级pango 3. 重新启动EasyConnect,即可成功启动 Ubuntu20.04安装EasyConnect后无法启动的解决方案 工作使用操 ...

  2. php背景透明png

    php背景透明png php处理图片时,例如生成水印,对于png的水印经常背景会加有色的背景,用此方法可以去除背景 主要函数:imagecolortransparent: //添加水印 $src = ...

  3. tensorboard No dashboards are active for the current data set.

    修改一下启动命令时的路径 位置示例: 命令为   E:\PYTHON_PROJECT\testTF\inceptionV1_net\log>tensorboard --logdir=TEC4FN ...

  4. day02 Rsyuc备份服务器

    day02 Rsyuc备份服务器 一.备份 1.什么是备份 备份就是把重要的数据或者文件复制一份保存到另一个地方,实现不同主机之间的数据同步 一般数据比较重要的情况下,数据如果丢失很容易找不回来了的, ...

  5. 数仓day04----日志预处理2

    1.详细描述idmap的整个计算方案 (1)使用SparkSession对象读取用户不同类别的埋点日志,解析并抽取出相应的标识id,使用union进行合并,得到装有汇总标识id的rdd(ids) (2 ...

  6. 剑指 Offer 10- I. 斐波那契数列

    写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N)).斐波那契数列的定义如下: F(0) = 0,   F(1) = 1F(N) = F(N - 1) + F(N ...

  7. STM32一些特殊引脚做IO使用的注意事项

    1 PC13.PC14.PC15的使用 这三个引脚与RTC复用,<STM32参考手册>中这样描述: PC13 PC14 PC15需要将VBAT与VDD连接,实测采用以下程序驱动4个74HC ...

  8. Oracle—网络配置文件

    Oracle网络配置文件详解     三个配置文件 listener.ora.sqlnet.ora.tnsnames.ora ,都是放在$ORACLE_HOME/network/admin目录下. 1 ...

  9. Oracle中dbms_random包详解

    Oracle之DBMS_RANDOM包详解参考自:https://www.cnblogs.com/ivictor/p/4476031.html https://www.cnblogs.com/shen ...

  10. 深入理解java动态代理机制

    动态代理其实就是java.lang.reflect.Proxy类动态的根据您指定的所有接口生成一个class byte,该class会继承Proxy类,并实现所有你指定的接口(您在参数中传入的接口数组 ...