【C++11】函数的可变参数模板

news2024/12/19 1:33:52

文章目录

  • 一. 为什么要有可变参数模板?
  • 二. 什么是可变参数模板?
  • 三. 如何展开参数包?
    • 1. 递归函数方式展开参数包
    • 2. 通过初始化列表展开参数包

一. 为什么要有可变参数模板?

C++98/03 中的模板为能够实现泛型编程提供了便利,但它们仍有不足之处:
在这里插入图片描述

有没有什么统一的办法能够让我们的类模版和函数模版中的参数不再固定呢?这样我们就可以不用根据参数个数再去依次定义多个它们对应的模板了。

C++11 的新特性可变参数模板能够让你表示任意个数、任意类型的参数的函数模板和类模板,相比 C++98/03:类模版和函数模版中只能含固定数量和固定类型的模版参数,可变参数模版无疑是一个巨大的改进。

二. 什么是可变参数模板?

下面就是一个基本可变参数的函数模板

/*
- Args是一个模板参数包
- args是一个函数形参参数包
- 声明一个参数包 Args...args,这个参数包中可以包含0到任意个模板参数
*/
template <class ...Args>
void ShowList(Args... args)
{}

上面的参数 args 前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数包”,它里面包含了0到N(N>=0)个模版参数。

我们可以实例化上面的函数模板:传入任意类型、数量的参数

#include <iostream>    
using namespace std;    
    
template <class ...Args>    
void ShowList(Args... args)    
{}    
    
int main()    
{    
  // 1、向参数包传入一个整型参数    
  ShowList(1);    
  // 2、传入一个整型和一个字符    
  ShowList(1, 'A');    
  // 3、传入一个整型1、字符'A'、字符串"sort"    
  ShowList(1, 'A', std::string("sort"));    
  return 0;    
}

此外我们还能够在可变参数的函数模板中利用sizeof...(参数包名称)计算其参数包中包含的参数个数:
在这里插入图片描述
编译运行:
在这里插入图片描述

三. 如何展开参数包?

关于参数包里的各个参数,我们是无法直接获取到的,只能通过展开参数包的方式,来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点,也是最大的难点,即如何获取到每个参数。由于语法不支持使用 args[i] 这样取下标的方式获取可变参数,所以我们需要用一些奇招来获取到参数包的值。
在这里插入图片描述

1. 递归函数方式展开参数包

既然不能直接从参数包中拿到参数,那我们考虑在 ShowList 函数中再引入一个 T 类型的模板参数 value,用来专门接收传入的第一个参数,这样其他 n-1 个参数就保存到了参数包中;然后用参数包继续递归调用 ShowList ,依次拿到后面的其他参数。

根据上面的想法,写出如下函数:

// 展开函数    
template <class T, class ...Args>    
void ShowList(T value, Args... args)    
{  
  // 1、传入的第一个参数保存到value中  
  cout << value <<" ";
  // 2、递归继续拿参数包中的参数    
  ShowList(args...);    
}   
 
int main()    
{     
  ShowList(1, 'A', std::string("sort"));    
  return 0;    
}  

递归调用图:
在这里插入图片描述

现在能够拿到参数包中的所有参数了,但是参数全拿完后这个递归如何停止呢?能否通过检查参数包中数据的个数来终止递归呢?

在这里插入图片描述

编译后会报错。因为这是个函数模板,编译阶段,编译器只负责推演模板的类型并配合进行基本的语法检查,而橙色框框起来的 if 逻辑代码是运行时才会执行的;实际编译时编译器不会管这段代码而直接推演下一次递归的类型和进行语法检查。
在这里插入图片描述

既然问题在于函数模板,我们考虑再实现一个 ShowList 同名的无参函数,这样当最后参数包为空时,系统会最优先匹配到这个无参的 ShowList 函数,然后在它里面进行 return 结束递归:

#include <iostream>    
using namespace std;    
    
// 递归终止函数(无参时最优先匹配该函数)    
void ShowList()    
{    
  cout<<endl;    
  // 在这里结束递归逻辑    
}    
    
// 展开函数(依次获取参数包的参数)    
template <class T, class ...Args>    
void ShowList(T value, Args... args)    
{    
  cout << value <<" ";    
  ShowList(args...);    
}    
    
int main()    
{    
  ShowList(1);    
  ShowList(1, 'A');    
  ShowList(1, 'A', std::string("sort"));    
  return 0;    
}  

编译运行:
在这里插入图片描述

2. 通过初始化列表展开参数包

这种展开参数包的方式,不需要通过递归终止函数,而是通过初始化列表来初始化一个变长数组,从而将参数包展开的,PrintArg(...)不是一个递归终止函数,只是一个处理参数包中每一个参数的函数。这种就地展开参数包的方式实现的关键是初始化列表。

// 处理不传参的情况
void ShowList()
{}

// 处理参数包中每一个参数
template <class T>
int PrintArg(T t)
{
	cout << t << " ";
	return 0;
}

// 通过初始化列表获取参数
template <class ...Args>
void ShowList(Args... args)
{
	int arr[] = { PrintArg(args)... };
	cout << endl;
}

int main()
{
	ShowList();
	ShowList(1, 'A');
	ShowList(1, 'A', std::string("sort"));
	return 0;
}

运行结果
在这里插入图片描述

解释说明
在这里插入图片描述

扩展:通过初始化列表和逗号表达式来张开参数包

// 处理参数包中每一个参数
template <class T>
void PrintArg(T t)
{
	cout << t << " ";
}

// 初始化列表 + 逗号表达式
template <class ...Args>
void ShowList(Args... args)
{
	int arr[] = { (PrintArg(args), 0)... };
	cout << endl;
}

解释说明
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1121873.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

购买腾讯云服务器如何领取优惠券?

随着云计算技术的不断发展&#xff0c;越来越多的个人和企业开始选择云服务器作为其业务发展的基础。作为国内领先的云计算服务商&#xff0c;腾讯云服务器以其稳定、安全、灵活的特点&#xff0c;受到了广大用户的青睐。为了更好地回馈用户&#xff0c;腾讯云服务器经常会推出…

C++ 类和对象(六)赋值运算符重载

1 运算符重载 C为了增强代码的可读性引入了运算符重载&#xff0c;运算符重载是具有特殊函数名的函数&#xff0c; 也具有其返回值类型&#xff0c;函数名字以及参数列表&#xff0c;其返回值类型与参数列表与普通的函数类似。 函数名字为&#xff1a;关键字operator后面接需…

并发编程-Java内存模型

Java内存模型 在并发编程中&#xff0c;需要处理的两个关键问题&#xff1a; 1&#xff09;多线程之间如何通信&#xff08;线程之间以何种机制来交换数据&#xff09; 2&#xff09;多线程之间如何同步&#xff08;控制不同线程间操作发生的相对顺序&#xff09; 线程之间…

AMD移动FP5平台时序解释

由于刚开始接触AMD移动平台,难免有错误;如有错误请指出共同进步。 配置如下: APU:FP5 Processor EC: ITE5570 CorePMU:RT3662AC 一、基本知识概括 1、IC pin脚信号解释 (1)一般OK是外部信号(对于IC来说是Input);PG是IC发出的(对于IC来说是Output);AMD开机:EN---…

存储优化知识复习三详细版解析

存储优化 知识复习三 一、 选择题 1、 数据库领域的三位图灵奖得主是( )。 A、C.W.Bachman B、E.F.Codd C、Peter Naur D、James Gray 【参考答案】ABD2、 数据库DB、数据库系统DBS、数据库管理系统DBMS三者之间得关系是&#xff08; &#xff09;。 A、&#xff24;B&#…

linux性能分析(四)性能优化导轮

一 别再让Linux性能问题成为你的绊脚石 ① 学习历程 备注&#xff1a; 跟我的学习经历很相像刚工作时遇到的场景跟我现在一样,深深的无力感驱使我继续前行目标: 性能优化成为我的肌肉记忆,写代码的潜意识 ... ② 常见的问题 ③ 性能问题为什么这么难呢 思考&#xff1a…

【驱动开发】创建设备节点、ioctl函数的使用

一、控制三盏灯的亮灭 头文件&#xff1a; #ifndef __HEAD_H__ #define __HEAD_H__ typedef struct{unsigned int MODER;unsigned int OTYPER;unsigned int OSPEEDR;unsigned int PUPDR;unsigned int IDR;unsigned int ODR; }gpio_t; #define PHY_LED1_ADDR 0X50006000 #def…

java读取指定文件夹下的全部文件,并输出文件名,文件大小,文件创建时间

import java.io.IOException; import java.nio.file.*; import java.nio.file.attribute.*; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { try { Path startingDir Paths.get("你的目…

带你了解如何防御DDoS攻击

DDoS攻击的类型和方法 分布式拒绝服务攻击&#xff08;简称DDoS&#xff09;是一种协同攻击&#xff0c;旨在使受害者的资源无法使用。它可以由一个黑客组织协同行动&#xff0c;也可以借助连接到互联网的多个受破坏设备来执行。这些在攻击者控制下的设备通常称为僵尸网络。 …

人工智能、机器学习、深度学习的区别

人工智能涵盖范围最广&#xff0c;它包含了机器学习&#xff1b;而机器学习是人工智能的重要研究内容&#xff0c;它又包含了深度学习。 人工智能&#xff08;AI&#xff09; 人工智能是一门以计算机科学为基础&#xff0c;融合了数学、神经学、心理学、控制学等多个科目的交…

表白墙完善(数据库,前端,后端Servlet),再谈Cookie和Session。以及一个关于Cookie的练习小程序

目录 表白墙引入数据库 再谈Cookie和session 得到Cookie ​编辑 设置Cooie 使用Cookie编写一个登入的小界面 表白墙引入数据库 1.先引入数据库的依赖&#xff08;驱动包&#xff09;&#xff0c;5.1.49 pom.xml中&#xff0c;在之前的两个之前&#xff0c;再去添加一个 &…

百度松果20231022作业

越狱 盒子与球 斯特林第二类数&#xff08;用dp求&#xff09;*盒子的阶乘 int dp[11][11]; //n>k int A(int x){int res1;fer(i,2,x1)res*i;return res; } signed main(){IOS;dp[2][1]dp[2][2]dp[1][1]1;fer(i,3,11){dp[i][1]1;fer(j,2,i){dp[i][j]j*dp[i-1][j]dp[i-1][j-…

搜一搜文章排名上首页无压力,优化这5点助你稳步上升

随着微信搜一搜的普及,如何提高文章在搜一搜中的排名已经成为许多公众号运营者关注的重点。高质量的搜一搜排名不仅可以带来更多的阅读量,也是提升公众号影响力的关键。那么,如何做才能轻松实现搜一搜文章上首页排名呢?更多搜一搜文章排名相关&#xff0c;可某薇找我名字。只需…

大规模语言模型人类反馈对齐--PPO算法代码实践

在前面的章节我们已经知道&#xff0c;人类反馈强化学习机制主要包括策略模型、奖励模型、评论模型以及参考模型等部分。需要考 虑奖励模型设计、环境交互以及代理训练的挑战&#xff0c; 同时叠加大语言模型的高昂的试错成本。对于研究人员来说&#xff0c; 使用人类反馈强化学…

数字孪生智慧建筑可视化系统,提高施工效率和建造质量

随着科技的不断进步和数字化的快速发展&#xff0c;数字孪生成为了建筑行业的一个重要的概念&#xff0c;被广泛应用于智能化建筑的开发与管理中。数字孪生是将现实世界的实体与数字世界的虚拟模型进行连接和同步&#xff0c;从而实现实时的数据交互和模拟仿真。数字孪生在建筑…

【Java 进阶篇】深入了解 Bootstrap 插件

Bootstrap 是一个流行的前端框架&#xff0c;提供了各种强大的插件&#xff0c;用于增强网页和应用程序的功能和交互性。本篇博客将深入介绍 Bootstrap 插件&#xff0c;适用于那些刚刚开始学习前端开发的小白。 什么是 Bootstrap&#xff1f; 在深入探讨 Bootstrap 插件之前…

QListWiget和QToolButton

1.简介 Qt 中用于项&#xff08;Item&#xff09;处理的组件有两类&#xff0c;一类是 Item Views&#xff0c;包括 QListView、QTreeView、 QTableView、QColumnView 等&#xff1b;另一类是 Item Widgets&#xff0c;包括 QListWidget、QTreeWidget 和 QTable Widget。 Ite…

手机知识:安卓内存都卷到24GB了,为何iPhone还在固守8GB

目录 一、系统机制 二、生态差异 三、总结 在刚刚过去的9月&#xff0c;年货iPhone 15系列正式发布&#xff0c;标准版不出意外还是挤药膏&#xff0c;除了镜头、屏幕有些升级&#xff0c;芯片用iPhone 14 Pro系列的&#xff0c;内存只有6GB&#xff1b;即使是集钛合金机身、…

Json数据上传—>对象转换—>存入MongoDB(SpringData提供的规范)

上传json 代码实现 RestController RequestMapping("/api/hosp") public class ApiController{Autowiredprivate HospitalService hospitalService;PostMapping("saveHospital")public Result saveHosp(HttpServletRequest request){Map<String,String…

Jetpack:015-Jetpack的是脚手架

文章目录 1. 概念介绍2. 使用方法2.1 核心思想2.2 具体内容 3. 示例代码4. 内容总结 我们在上一章回中介绍了Jetpack中小红点相关的内容&#xff0c;本章回中将介绍 脚手架。闲话休提&#xff0c;让我们一起Talk Android Jetpack吧&#xff01; 1. 概念介绍 我们在本章回中介…