C++11 可变参数模板

news2024/12/29 7:35:15

作者:@小萌新
专栏:@C++进阶
作者简介:大二学生 希望能和大家一起进步!
本篇博客简介:介绍C++11的可变参数模板

可变参数模板

    • 可变参数模板的概念
    • 可变参数模板的定义方式
    • 参数包两种解开方式
      • 递归展开参数包
      • 逗号表达式展开参数包

可变参数模板的概念

可变参数是C++11新增的比较重要的特性之一

它对参数高度泛化 能够让我们创建接受可变参数的函数模板还有类模板

  • 在C++11之前模板只能使用固定个数参数 所以可变参数模板特性无疑是一个巨大的进步 但是可变参数模板比较抽象 使用起来需要一定的技巧
  • 其实在C++11之前就已经有了可变参数这个概念 比如说库函数中的printf函数 它就可以接受多个参数 但是这只是函数参数的可变参数 并不是模板的可变参数

可变参数模板的定义方式

在C++11中 可变参数模板的定义方式如下

template<class …Args>
返回类型 函数名(Args… args)
{
  //函数体
}

我们写出一个特化版本的函数

template<class ...Args>
void ShowList(Args... args)
{}

对于上面的代码我们这里予以解释

  • 模板参数 …Args 前面的省略号代表这是一个可变参数模板 我们将带有省略号的参数称为参数包 参数包里面可以有0~N(N > 0)个参数 args是参数包的形参
  • 它们的名字并不是一定要叫Args和args 可以修改为别的名称

使用了这个模板之后我们就可以使用多种参数来调用这个函数

	ShowList();
	ShowList(1);
	ShowList(1, 'A');
	ShowList(1, 'A', string("hello"));

在这里插入图片描述
我们还可以通过sizeof来计算函数到底调用了多少个参数

template<class ...Args>
void ShowList(Args... args)
{
	cout << sizeof...(args) << endl;
}

这里有一点特别重要 sizeof后面的…不能省略 否则编译器会报错

在这里插入图片描述
修改正确后的运行结果如下

在这里插入图片描述

虽然我们能很简单的通过sizeof关键字来获取参数包中参数的个数

但是我们缺很难知道参数包中每个参数的类型 这也是参数包使用中的一大难点

需要注意的是 我们不能通过下标访问操作符【】来访问参数包

在这里插入图片描述
接下来我们会介绍两种获取参数包中各个参数的方式

参数包两种解开方式

递归展开参数包

展开参数包的思路如下

  • 我们给函数模板增加一个参数T 它的作用是分离出一个参数出来
  • 在函数模板中递归调用该函数模板 调用时传入剩下的参数包
  • 依次递归 直到所有的参数都被分离一次

代码表示如下

template<class T,class ...Args>
void ShowList(T value,Args... args)
{
	cout <<typeid(value).name() << endl;
	ShowList(args...);
}

但是这里我们会面临一个问题 这个问题是假如我们递归到了最后的无参

就没有ShowList函数能够匹配这个实例了

所以说我们必须还要再加一个无参版本的ShowList函数

void ShowList()
{
	cout <<endl;
}

这样一来递归到最后的无参版本就会终止递归

这里还有两个注意点

  • 如果外部调用ShowList函数时就没有传入参数 那么就会直接匹配到无参的递归终止函数
  • 我们本意是想让外部调用ShowList函数时匹配的都是函数模板 并不是让外部调用时直接匹配到这个递归终止函数

所以说我们可以针对代码做以下改进

void ShowListArgs()
{
	cout << endl;
}

template<class T,class ...Args>
void ShowListArgs(T value,Args... args)
{
	cout <<typeid(value).name() << endl;
	ShowListArgs(args...);
}

template<class ...Args>
void ShowList(Args ...args)
{
	ShowListArgs(args...);
}

我们可以将展开函数和递归调用函数的函数名改为ShowListArgs

并且重新写一个模板函数 使用函数包作为参数

这样子不管传入多少的参数匹配的函数就都是ShowList了

演示结果如下

在这里插入图片描述

逗号表达式展开参数包

我们都知道 数组可以使用列表初始化

比如说像这样

  int a[] = {1,2,3,4}

除此之外 如果参数包中各个参数的类型都是整型 那么也可以把这个参数包放到列表当中初始化这个整型数组 此时参数包中参数就放到数组中了

代码表示如下

template<class ...Args>
void ShowList(Args ...args)
{
	int arr[] = { args... };
	// 这里展开args省略号要在后面

	for (auto e : arr)
	{
		cout << typeid(e).name() << " ";
	}

	cout << endl;
}

此时我们就可以像ShowList中传入多参数了 代码和演示结果如下

	ShowList(1);
	ShowList(1,2,3);
	ShowList(1,2,3,4);

在这里插入图片描述
但是C++规定 一个容器中只能容纳一种类型的数据 所以当我们传入多种类型的参数就会发生下面的情况
在这里插入图片描述
这个时候提出的一个解决方案就是逗号表达式

逗号表达式 运行完所有的参数之后返回最后一个值

这样子只要我们设置最后一个值为一个整型 然后再创建一个新的函数来接受参数包的每个数据就好了

代码表示如下

// 模板函数打印出参数包中每个参数的类型
template<class T>
void Print(T& t)
{
	cout << typeid(t).name() << " ";
}

template<class ...Args>
void ShowList(Args ...args)
{
	// 这里展开args省略号要在后面
	int arr[] = { (Print(args),0)... };
	cout << endl;
}

试验数据和演示结果如下

	ShowList(1,string("hello"));
	ShowList(1, 1.1);
	ShowList(1, 2, 3, 4);

在这里插入图片描述
但是此时仍然存在者一个问题 那就是在C++中 这段代码是会报错的

   int arr[] = {};

C++语法规定 我们不能分配常量大小为0的数组

这个问题反应到我们现在写的代码上就是我们无法在无参的情况下调用函数

在这里插入图片描述

解决这个问题的方式也很简单 我们写出一个特化版本的函数就可以

void ShowList()
{
	cout << endl;
}

在这里插入图片描述

还有就是关于省略逗号表达式的问题

逗号表达式存在的意义只是为了让容器中的类型统一 那么只要类型原本就是统一的 我们就能够省略逗号表达式

具体的思路如下

我们让Print函数返回一个int类型的值 之后在列表中展开即可

代码表示如下

template<class T>
int Print(T& t)
{
	cout << typeid(t).name() << " ";
	return 0;
}

void ShowList()
{
	cout << endl;
}

template<class ...Args>
void ShowList(Args ...args)
{
	// 这里展开args省略号要在后面
	int arr[] = { Print(args)... };
	cout << endl;
}

我们还是用之前相同的测试样例 测试后结果如下

在这里插入图片描述

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

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

相关文章

Springboot在线考试管理系统

一、项目简介 本项目是一套基于Springboot在线考试管理系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;…

java基于springboot+vue的校园跑腿系统

随着我国教育制度的改革的发展&#xff0c;各大高校的学生数量也在不断的增加。当前大学生的生活和消费习惯等导致他们更喜欢通过网络来获取自己想要的商品和服务&#xff0c;这也是导致当前校园跑腿盛行的主要原因。为了能够让更多的学生享受到校园跑腿的服务&#xff0c;我们…

中睿天下荣获中国网络安全产业联盟(CCIA)2022年度先进会员单位奖

近日&#xff0c;中国网络安全产业联盟&#xff08;CCIA&#xff09;发布“关于2022年度表彰先进的决定”&#xff0c;对在联盟工作中做出积极贡献的会员单位进行表彰&#xff0c;中睿天下荣获中国网络安全产业联盟“2022年度先进会员单位“奖。中国网络安全产业联盟成立于2015…

Linux基本使用

文章目录1. Linux 基本命令文件内容查看命令wget下载tar.gz文件tar压缩解压缩yum2. 安装node/npm下载解压配置环境变量设置npm代理3. 权限1. Linux 基本命令 ls&#xff1a;用来查看当前目录文件 # 查看隐藏文件 -a ls -a # 查看详细信息 -l 简写为 ll ls -l ll # 查看指定目…

Docker - 8. Docker镜像底层原理

目录 1. 镜像是什么 2. UnionFS 联合文件系统 3. Docker镜像加载原理 4. Docker 容器和镜像的底层关系 5. Docker 镜像 commit 操作案例 1. 镜像是什么 Docker 镜像是一个特殊的文件系统&#xff0c;除了提供容器运行时所需的程序、库、资源、配置等文件外&#xff0c;还…

漏洞深度分析|Apache iotdb-web-workbench 认证绕过漏洞(CVE-2023-24830)分析

项目介绍 IoTDB-Workbench是IoTDB的可视化管理工具&#xff0c;可对IoTDB的数据进行增删改查、权限控制等。 项目地址 https://github.com/apache/iotdb-web-workbench 漏洞概述 Apache iotdb-web-workbench 由于硬编码了jwt token加密密钥&#xff0c;导致攻击者可以伪造t…

数仓之缓慢变化维(拉链表)

文章目录缓慢变化维拉链表 -- 理论缓慢变化维解决方案&#xff1a;拉链表场景&#xff1a;拉链表缺点&#xff1a;拉链表查询优化&#xff1a;拉链表 -- 示例sql查询方式补充流水表全量表增量表缓慢变化维 什么是缓慢变化维? 缓慢变化维&#xff0c;简称SCD&#xff08;Slowl…

科技云报道:上云尚未成功,“下云潮”已悄然来临?

科技云报道原创。 云计算一直被视为是企业数字化转型的底座&#xff0c;很多企业都在通过加速数字化转型应对市场环境的动荡变化&#xff0c;一手抓降本增效&#xff0c;另一手也还在继续谋求突破式创新。 然而&#xff0c;经历这两年的疫情&#xff0c;活下去成为每一个企业的…

【js逆向 | 网络爬虫】简单 js 逆向实践,获取邮箱地址

网站&#xff1a; https://australia51.com/citywide/ 在各个城市分类下&#xff0c;可以看到商家链接 而部分商家介绍中&#xff0c;含有邮箱 我们通过 f12 检查元素&#xff0c;发现邮箱字段在 html 里面。 但是抓包发现&#xff0c;该字段是 <a href"/cdn-cg…

Anaconda环境Python中xlrd库的配置方法

本文介绍在Anaconda环境下&#xff0c;安装Python读取.xls格式表格文件的库xlrd的方法。 首先需要说明的是&#xff0c;由于xlrd库在读取.xlsx格式文件时具有安全漏洞&#xff0c;因此在其2.0.0及以后的版本中&#xff0c;只能读取.xls格式的表格文件&#xff1b;如果需要读取其…

C语言中字符串库函数

目录1.求字符串长度strlen模拟实现strlen2.长度不受限制的字符串函数strcpy模拟实现strcpystrcat模拟实现strcatstrcmp模拟实现strcmp3.长度受限制的字符串函数介绍strncpy模拟实现strncpystrncat模拟实现strncatstrncmp模拟实现strncmp4.字符串查找strstr模拟实现strstrstrtok…

POE交换机全方位解读(下)

POE供电在安防系统中的应用 安防行业应用PoE组网主要有简化布线、节能灵活、安全方便等优势。众所周知&#xff0c;一般的网络摄像机除了需要通过网线来传输视频信号外&#xff0c;还必须全天候为其提供足够的电力。而在现实施工中&#xff0c;经常会出现因为无法提供稳定的电源…

[SSL]微信实机测试 request:fail -2:netLERR_FAILED

request:fail -2:netLERR_FAILED 微信小程序开发过程中&#xff0c;实机测试调用服务器链接报错。 SSL证书已安装&#xff0c;通过下面工具检查&#xff0c;中间证书安装有问题。 下面是检查证书链和补全证书链的工具网站&#xff0c;亲测有效。 w​​​​​​​​​​​​​…

[数仓]埋点数据接入

第40个视频的1&#xff1a;03&#xff1a;31一、采集flume日志服务器&#xff1a;将日志采集到本地&#xff0c;共有两个日志服务器&#xff0c;因此要安装两台flume&#xff0c;每个flume采集其所在服务器上的日志source:taildir source可以实时的读取文件中的数据&#xff0c…

详解Linux中网络文件系统

目录 前言 一、samba服务简介 1、windos如何共享文件 2、在linux中访问共享文件 二、samba基本信息 三、samba的安装与启用 1.服务端 2.客户端 3.服务启用 四、建立samba服务共享目录 五、samba用户的建立 六、 samba用户访问加目录 七、samba的访问控制 八、 sa…

Web(十一)

Request 1. request对象和response对象的原理 1. request和response对象是由服务器创建的。我们来使用它们 2. request对象是来获取请求消息&#xff0c;response对象是来设置响应消息 2. request对象继承体系结构&#xff1a; ServletRequest -- 接…

Kafka知识概况

Kafka知识概况Kafka简介Kafka 生产者Kafka BrokerKafka 消费者Kafka-Eagle 监控Kafka-Kraft 模式集成 SpringBootKafka简介 消息队列简介&#xff1a; 目 前企业中比较常见的消息队列产 品主 要有 Kafka、ActiveMQ 、RabbitMQ 、RocketMQ 等。在大数据场景主要采用 Kafka 作为…

【ElasticSearch8.X】学习笔记(一)

【ElasticSearch8.X】学习笔记一、8.x与7.x的对比二、安装elk8.x2.1、下载2.2、集群规划2.3、安装2.4、配置环境2.5、修改配置文件2.6、启动2.5、安装其他结点三、Kibana 安装3.1、下载3.2、配置环境3.2、修改配置文件3.4、启动一、8.x与7.x的对比 减少内存堆使用&#xff0c;…

JavaScript 进阶--charater2

系列文章目录 提示&#xff1a; JavaScript进阶笔记 &#xff0c;希望各位看官可以高抬小手一键三连 上一章测试题 答案在最后给出 文章目录系列文章目录前言一、深入对象1.1创建对象三种方式1. 利用对象字面量创建对象2.利用new Object 创建对象3. 利用构造函数创建对象1.2 构…

设计师百度百科词条创建怎么做?

设计分为平面设计、空间设计、工业设计、珠宝设计、游戏设计、家具设计、建筑设计、室内设计、景观设计、服装设计、网页设计、系统设计、剧场设计、动漫设计、品牌设计、造型设计、三维设计师、杂志封面设计师、包装设计师、形象设计师等领域。 设计师是一个提供创意的工作&a…