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我们下期再见~!

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

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

相关文章

微服务Docker相关指令

1、拉取容器到镜像仓库 docker pull xxx //拉取指令到 镜像仓库 例如 docker pull mysql 、docker pull nginx docker images //查看镜像仓库 2、删除资源 2.1、删除镜像仓库中的资源 docker rmi mysql:latest //删除方式一&#xff1a;格式 docker rmi 要…

【解密 Kotlin 扩展函数】扩展函数的创建(十六)

导读大纲 1.1 为第三方的类添加方法: 扩展函数 1.1 为第三方的类添加方法: 扩展函数 Kotlin 的主题之一是与现有代码的平滑集成 即使是纯 Kotlin 项目,也是构建在 Java 库之上的 如 JDK、Android 框架和其他第三方框架 而当你将 Kotlin 集成到 Java 项目中时 你还要处理尚未或不…

python爬虫:将知乎专栏文章转为pdf

欢迎关注本人的知乎主页~ 实现思路 用户输入专栏ID&#xff1a; 代码首先提示用户输入一个知乎专栏的ID&#xff0c;默认值为 c_1747690982282477569。输入的ID用于构建API请求的URL。 发送HTTP请求&#xff1a; 使用 requests.get() 向知乎API发送GET请求&#xff0c;获取指定…

【QGIS入门实战精品教程】6.1:QGIS根据属性条件查询数据(SQL表达式)

文章目录 一、字段过滤二、高级过滤(表达式)一、字段过滤 对于单个字段的查询,可以采用字段过滤,例如,从县区数据中,根据NAME字段,查找出县级市玉门市。操作为:右键县区→打开属性表: 点击左下角,选择name字段。 输入玉门市,回车,选择查找除的属性表记录,此时图斑…

【Linux】入门【更详细,带实操】

Linux全套讲解系列&#xff0c;参考视频-B站韩顺平&#xff0c;本文的讲解更为详细 目录 1、课程内容 2、应用领域 3、概述 4、 Linux和Unix 5、VMware15.5和CentOS7.6安装 6、网络连接三种方式 7、虚拟机克隆 8、虚拟机快照 9、虚拟机迁移删除 10、vmtools 11、目录…

set-ExecutionPolicy RemoteSigned 提示不是内部或外部命令,也不是可运行的程序或批处理文件

这个错误一般发生在使用命令提示符或者PowerShell窗口中找不到set-ExecutionPolicy RemoteSigned。如果你想在命令提示符或者PowerShell窗口运行set-ExecutionPolicy RemoteSigned&#xff0c;你需要搜索打开Window PowerShell ISE&#xff0c;并以管理员身份打开&#xff0c;输…

基于微信小程序的美食外卖管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…

Tomcat系列漏洞复现

CVE-2017-12615——Tomcat put⽅法任意⽂件写⼊漏洞 漏洞描述 当 Tomcat运⾏在Windows操作系统时&#xff0c;且启⽤了HTTP PUT请求⽅法&#xff08;例如&#xff0c;将 readonly初始化参数由默认值设置为false&#xff09;&#xff0c;攻击者将有可能可通过精⼼构造的攻击请求…

身份安全风险不断上升:企业为何必须立即采取行动

在推动安全AI 模型的过程中&#xff0c;许多组织已转向差异隐私。但这种旨在保护用户数据的工具是否阻碍了创新&#xff1f; 开发人员面临一个艰难的选择&#xff1a;平衡数据隐私或优先考虑精确结果。差分隐私可以保护数据&#xff0c;但通常以牺牲准确性为代价——对于医疗保…

某省公共资源交易中心爬虫逆向分析

目标网站 aHR0cHM6Ly95Z3AuZ2R6d2Z3Lmdvdi5jbi8jLzQ0L3NjenQteHEvP3VzZXJJZD02NzM4OTg2MzkyNjA3NzAzMDQmcm93SWQ9NTI1MDYyMDI2ODg0NzE2NTQ0JnRpbWU9MjAwOC0xMS0yNiZjZXJ0aWZpY2F0ZU5vPTkxNDQwOTA0NjgyNDI2MzU4QyZjZXJ0aWZpY2F0ZVR5cGU9Mjg 一、抓包分析 请求头参数加密 二、…

【学习笔记】手写 Tomcat 五

目录 一、优化 Servlet 创建一个抽象类 继承抽象类 二、三层架构 业务逻辑层 数据访问层 1. 在 Dao 层操作数据库 2. 调用 Dao 层&#xff0c;实现业务逻辑功能 3. 调用 Service 层&#xff0c;响应数据 测试 三、数据库连接池 1. 手写数据库连接池 2. 创建数据库…

2024年9月19日---关于ES6(2)

五 异步编程 5.1 回调函数 5.1.1 概念 回调函数(callback function)&#xff0c;当一个函数作为参数传入另一个参数中&#xff0c;并且它不会立即执行&#xff0c;只有当满足一定条件后该函数才可以执行&#xff0c;这种函数就称为回调函数。 你可以将其理解为 回头再调用的…

GNU编译器(GCC):编译的4个过程及.elf、.list、.map文件功能说明

0 参考资料 GNU-LD-v2.30-中文手册.pdf GNU linker.pdf1 前言 一个完整的编译工具链应该包含以下4个部分&#xff1a; &#xff08;1&#xff09;编译器 &#xff08;2&#xff09;汇编器 &#xff08;3&#xff09;链接器 &#xff08;4&#xff09;lib库 在GNU工具链中&…

开源 AI 智能名片 S2B2C 商城小程序与营销工具的快速迭代

摘要&#xff1a;本文以开源 AI 智能名片 S2B2C 商城小程序为研究对象&#xff0c;探讨在营销工具快速迭代的背景下&#xff0c;该小程序如何借鉴以拼多多为代表的“小程序拼团”、以蘑菇街为代表的“小程序直播”、以花点时间为代表的“小程序按月订花”等经典案例&#xff0c…

数据集-目标检测系列-摩托车检测数据集 motorcycle >> DataBall

数据集-目标检测系列-摩托车检测数据集 motorcycle >> DataBall 数据集-目标检测系列-摩托车&#xff08;motorcycle&#xff09;检测数据集 数据量&#xff1a;1W 想要进一步了解&#xff0c;请联系 DataBall。 DataBall 助力快速掌握数据集的信息和使用方式&#xf…

《论软件架构建模技术与应用》写作框架,软考高级系统架构设计师

论文真题 软件架构风格是描述一类特定应用领域中系统组织方式的惯用模式&#xff0c;反映了领域中诸多系统所共有的结构特征和语义特征&#xff0c;并指导如何将各个模块和子系统有效组织成一个完整的系统。分层架构是一种常见的软件架构风格&#xff0c;能够有效简化设计&…

Python语法进阶之路

一、Python基础 1.1 注释 定义和作用 对代码解释说明&#xff0c;增强可读性 单行注释 # 多行注释 """ 这是一个多行注释 """ 1.2 变量及变量类型 定义和作用 计算机目的是计算&#xff0c;编程是为了更方便计算&#xff0c;计算对象就是…

论前端框架的对比和选择 依据 前端框架的误区

前端框架的对比和选择依据 在前端开发中&#xff0c;有多种框架可供选择&#xff0c;以下是一些常见前端框架的对比和选择依据&#xff1a; 一、Vue.js 特点&#xff1a; 渐进式框架&#xff0c;灵活度高&#xff0c;可以逐步引入到项目中。学习曲线相对较平缓&#xff0c;容…

Attention is All You Need精读

原文开头&#xff0c;不是搬运 dog。All attention is all you need Abstract 摘要 这篇文章的内容源于基于编码器-解码器架构的RNN模型&#xff0c;在其他人的工作中&#xff0c;我们可以发现注意力机制对于提升编码器-解码器架构模型的性能很重要。这篇文章提出了一个比较简…

SpringBoot 更改启动图标

每次springboot开启时&#xff0c;开会显示spring的图标&#xff0c;这个图标是可以修改的。 一、原始的图标样式 二、修改图标样式 https://patorjk.com/software/taag/#pdisplay&fGraffiti&thttps://patorjk.com/software/taag/#pdisplay&fGraffiti&t 在…