当前位置: 首页 > news >正文

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的新特性&#xff0c;本期我们介绍的这个新特性是很多人都感觉抽象的语法&#xff01;它就是可变的模板参数&#xff01; 目录 前言 一、可变的模板参数 1.1可变的参数列表 1.2可变的参数包 1.3可变参数包的解析 • 递归展开解析 • 逗号…...

手机在网状态查询接口如何用PHP进行调用?

一、什么是手机在网状态查询接口&#xff1f; 手机在网状态查询接口&#xff0c;即输入手机号码查询手机号在网状态&#xff0c;返回有正常使用、停机、在网但不可用、不在网&#xff08;销号/未启用/异常&#xff09;、预销户等多种状态。 二、手机在网状态查询适用哪些场景…...

MATLAB中多张fig图合并为一个图

将下列两个图和为一个图 打开查看-----绘图浏览器 点击第一幅图中曲线右键复制&#xff0c;到第二幅图中粘贴即可完成...

Java启动Tomcat: Can‘t load IA 32-bit .dll on a AMD 64-bit platform报错问题解决

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…...

基于微信小程序的家教信息管理系统的设计与实现(论文+源码)_kaic

摘 要 随着互联网时代的来临&#xff0c;使得传统的家教模式已不复存在&#xff0c;亟需一种方便、快捷的在线教学平台。因此&#xff0c;利用Java语言作为支撑和MySQL数据库存储数据&#xff0c;结合微信小程序的便利性&#xff0c;为用户开发出了一个更加人性化、方便的家庭…...

【Android】BottomSheet基本用法总结(BottomSheetDialog,BottomSheetDialogFragment)

BottomSheet BottomSheet 是一种位于屏幕底部的面板&#xff0c;用于显示附加内容或选项。提供了从屏幕底部向上滑动显示内容的交互方式。这种设计模式在 Material Design 中被广泛推荐&#xff0c;因为它可以提供一种优雅且不干扰主屏幕内容的方式来展示额外信息或操作。 具体…...

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…...

【中国留学网-注册_登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…...

jvm中的程序计数器、虚拟机栈和本地方法栈

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

安卓数据存储——SharedPreferences

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

【计算机网络篇】数据链路层 功能|组帧|流量控制与可靠传输机制

&#x1f9f8;安清h&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;【计算机网络】 &#x1f6a6;作者简介&#xff1a;一个有趣爱睡觉的intp&#xff0c;期待和更多人分享自己所学知识的真诚大学生。 系列文章目录 【计算机网络篇】计算机网络概述 【计算机网络篇…...

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、工具验证...

带线无人机现身俄罗斯抗干扰技术详解

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

ArcGIS10.2/10.6安装包下载与安装(附详细安装步骤)

相信从事地理专业的小伙伴来说&#xff0c;应该对今天的标题不会陌生。Arcgis是一款很常用的地理信息系统软件&#xff0c;主要用于地理数据的采集、管理、分析和展示。目前比较常见的版本有ArcGIS 10.2和ArcGIS 10.6。 不可否认&#xff0c;Arcgis具有强大的地图制作、空间分…...

生信服务器 | 组蛋白甲基化修饰、DNA亲和纯化测序、优青博导团队指导设计、解读实验结果。

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

【machine learning-14-特征缩放-归一化】

特征缩放是提升线性回归收敛速度的技巧&#xff0c;什么是特征缩放&#xff1f; 又是什么场景下需要特征缩放&#xff0c;有哪些特征缩放的方法呢&#xff1f; 特征值差异 我们还是以之前房间预测为例&#xff1a; 这里面是特征房屋大小 房间数目 与房价的关系 本文为简化…...

二叉树堆的建立与排序

在数据结构中&#xff0c;二叉树是非常好用的一种数据结构&#xff0c;这节暂时按下不表。这节课主要介绍堆的建立与使用。 堆&#xff0c;是二叉树中一种很特殊的结构&#xff0c;首先&#xff0c;他必须是满二叉树&#xff0c;也就是除了最后一层以外&#xff0c;其他层都是…...

【软件测试】Bug 篇

哈喽&#xff0c;哈喽&#xff0c;大家好~ 我是你们的老朋友&#xff1a;保护小周ღ 今天给大家带来的是 【软件测试】Bug 篇&#xff0c;首先了解, 什么是Bug, 如何定义一个Bug, 如何描述一个 Bug, Bug的级别, 和 Bug 的生命周期, 以及测试人员跟开发人员产生争执如何处理,…...

oracle 多表查询

3.6多表查询 当查询的数据并不是来源一个表时&#xff0c;需要使用多表连接操作完成查询。多表连接查询通过表之间的关联字段&#xff0c;一次查询出多个表的数据。 3.6.1等值连接 等值连接也称为简单连接(Simple Joins)或者内连接(Inner Join)。通过等号来判断连接条件中的数据…...

layui 可以使点击图片放大

layui可以使图片点击放大&#xff0c;不用在写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…...

制作网上3D展馆需要什么技术并投入多少费用?

制作网上3D展览馆项目&#xff0c;需要考虑以下技术和预算方面的信息&#xff1a; 技术需求&#xff1a; 1、三维建模技术&#xff1a;利用3D软件&#xff08;3ds max、maya、blender、c4d等&#xff09;制作展馆和展品的3D模型 2、Web3D技术&#xff1a;如WebGL&#xff0c…...

C++标准库容器类——string类

引言 在c中&#xff0c;string类的引用极大地简化了字符串的操作和管理&#xff0c;相比 C 风格字符串&#xff08;char*或cahr[]&#xff09;&#xff0c;std::string 提供了更高效和更安全的字符串操作。接下来让我们一起来深入学习string类吧&#xff01; 1.string 的构造…...

Qt --- 常用控件的介绍 --- 其他控件

一、QPushButton QWidget中设计到的各种属性/函数/使用方法&#xff0c;针对接下来要介绍的Qt的各种控件都是有效的。 使用QPushButton表示一个按钮&#xff0c;这也是当前我们最熟悉的一个控件了。这个类继承了QAbstractButton&#xff0c;这个类是一个抽象类&#xff0c;是…...

spark读取数据性能提升

1. 背景 spark默认的jdbc只会用单task读取数据&#xff0c;读取大数据量时&#xff0c;效率低。 2. 解决方案 根据分区字段&#xff0c;如日期进行划分&#xff0c;增加task数量提升效率。 /*** 返回每个task按时间段划分的过滤语句* param startDate* param endDate* param …...

一次使用threading.Thread来实现Pytorch多个模型并发运行的失败案例

文章目录 背景我的做法&#xff08;但证明不起效果&#xff09; 背景 我有多个pytorch GPU模型&#xff0c;他们有不同的参数&#xff08;也就是说不是共享的&#xff09;&#xff0c;但是相同的数据输入&#xff0c;想要并发运行。 不并发运行&#xff0c;当然就是循环喽。 …...

HashMap源码

简介 HashMap 是一种基于哈希表的 Map 接口实现&#xff0c;它存储键值对&#xff08;key-value pairs&#xff09;&#xff0c;并允许使用键来快速检索值。在 Java 中&#xff0c;HashMap 是 java.util 包的一部分&#xff0c;它不是同步的&#xff0c;这意味着它不是线程安全…...

探索 Web Speech API:实现浏览器语音识别与合成

引言 Web Speech API 是一项由 W3C 开发的 Web 标准&#xff0c;为开发者提供了在 Web 应用程序中实现语音识别和语音合成的能力。通过 Web Speech API&#xff0c;我们可以让网页与用户进行语音交互&#xff0c;实现更加智能化和便捷的用户体验。本文将深入探讨 Web Speech A…...

python基础题练习

1.可否定义一个sum函数呢&#xff1f;返回指定区间的值的和&#xff1f;例如&#xff0c;区间[1,4]的和为123410返回指定区间值的平方的和呢&#xff1f;立方呢&#xff1f; 代码&#xff1a; # 计算从start到end&#xff08;包括end&#xff09;的所有整数的和。 def sum_ra…...

工业交换机如何保证数据的访问安全

在现代工业自动化环境中&#xff0c;工业交换机作为关键的网络设备&#xff0c;扮演着数据传输和信息交互的重要角色。为了确保数据的访问安全&#xff0c;工业交换机不仅具备高效的转发性能&#xff0c;还集成了多层次的安全防护机制&#xff0c;以抵御各种潜在的网络威胁。 首…...

jmeter得到的文档数据处理

通过前面jmeter得到的输出文档&#xff0c;这里是txt文档&#xff0c;里面包含了很多条数据&#xff0c;每条数据的结构如下&#xff1a; 【request】 uuid&#xff1a;xxxxxxx timestamp&#xff1a;xxxxxxxx No.x question&#xff1a;xxxxxxx 【response】 code&#…...