【C++】C++11——可变参数模板和emplace

news2025/1/10 20:41:42

      • 可变参数模板的定义方式
      • 可变参数模板的传值
      • 计算可变参数模板参数个数
      • 参数包展开方式
        • 递归展开参数包
        • 逗号表达式展开参数包
      • emplace插入

可变参数模板是C++11新增的最强大的特性之一,它对参数高度泛化,能够让我们创建可以接受可变参数的函数模板和类模板。

  • 在C++11之前,类模板和函数模板中只能包含固定数量的模板参数,可变模板参数无疑是一个巨大的改进,但由于可变参数模板比较抽象,因此使用起来需要一定的技巧。
  • 在C++11之前其实也有可变参数的概念,比如printf函数就能够接收任意多个参数,但这是函数参数的可变参数,并不是模板的可变参数。

可变参数模板的定义方式

可变参数包的个数可以为0,也可以为N

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

如:

template<class ...Args>
void ShowList(Args... args)
{}
  • 模板参数包Args和函数形参参数包args名字可以任意指定,并不是强制的。

可变参数模板的传值

int main()
{
	ShowList();
	ShowList(1);
	ShowList(1,2);
	ShowList(1,2,"abc");
	return 0;
}

在可变参数模板中,我们可以传入任意类型

计算可变参数模板参数个数

template<class ...Args>
void ShowList(Args ...args)
{
	cout << sizeof...(args) << endl;//注意...的位置
}

int main()
{
	ShowList();
	ShowList(1);
	ShowList(1,2);
	ShowList(1,2,"abc");
	return 0;
}
  • 在可变参数模板中,一定要注意…的位置问题

在这里插入图片描述

参数包展开方式

递归展开参数包

使用递归方式展开参数包我们可以:

  • 给函数模板再添加一个模板参数,用于接受分离参数包中的首个参数。
  • 在函数模板中调用该模板函数,实参一直调用剩下的参数包
  • 如此递归下去,每次分离出参数包中的一个参数,直到参数包中的所有参数都被取出来。

综合以上的考虑方向,我们首先考虑到的就是当传入0个参数时怎么办?
这时需要我们设置一个专门接受0个参数的函数,来起到终止的作用

void ShowList()
{
	cout << "起到了终止的作用" << endl;
}

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

int main()
{
	ShowList();
	ShowList(1);
	ShowList(1,2);
	ShowList(1,2,"abc");
	return 0;
}

在这里插入图片描述

在这里插入图片描述
如果想要无论外部调用时传入多少个参数,最终匹配到的都是同一个函数了时,我们需要这样修改

//递归终止函数
void ShowListArg()
{
	cout << endl;
}
//展开函数
template<class T, class ...Args>
void ShowListArg(T value, Args... args)
{
	cout << value << " "; //打印传入的若干参数中的第一个参数
	ShowListArg(args...); //将剩下参数继续向下传
}
//供外部调用的函数
template<class ...Args>
void ShowList(Args... args)
{
	ShowListArg(args...);
}

也可以编写带参的退出函数,这样一来吗,当参数包剩下一个参数的时候,就会自动匹配退出函数

//递归终止函数
template<class T>
void ShowListArg(const T& t)
{
	cout << t << endl;
}
//展开函数
template<class T, class ...Args>
void ShowListArg(T value, Args... args)
{
	cout << value << " "; //打印传入的若干参数中的第一个参数
	ShowList(args...);    //将剩下参数继续向下传
}
//供外部调用的函数
template<class ...Args>
void ShowList(Args... args)
{
	ShowListArg(args...);
}

但该方法有一个弊端就是,我们在调用ShowList函数时必须至少传入一个参数,否则就会报错。因为此时无论是调用递归终止函数还是展开函数,都需要至少传入一个参数。

逗号表达式展开参数包

我们可以用一个数组来接收参数包。例如以下方式:

int a[] = {1,2,3};//列表初始化方式

我们可以通过调用参数包,将参数包里的内容通过列表初始化的方式放入数组中。如下
在这里插入图片描述
但是以上代码有一个问题,我们传入的参数必须为整型才配用这个逻辑,怎么解决这个问题呢?

逗号表达式解决

  • 将逗号表达式的最后一个表达式设置为一个整型值,确保逗号表达式返回的是一个整型值。
  • 将处理参数包中参数的动作封装成一个函数,将该函数的调用作为逗号表达式的第一个表达式。

这样一来,在执行逗号表达式时就会先调用处理函数处理对应的参数,然后再将逗号表达式中的最后一个整型值作为返回值来初始化整型数组。比如
在这里插入图片描述
实际上我们也可以不用逗号表达式,因为这里的问题就是初始化整型数组时必须用整数,那我们可以将处理函数的返回值设置为整型,然后用这个返回值去初始化整型数组也是可以的。比如:

//支持无参调用
void ShowList()
{
	cout << endl;
}
//处理函数
template<class T>
int PrintArg(const T& t)
{
	cout << t << " ";
	return 0;
}
//展开函数
template<class ...Args>
void ShowList(Args... args)
{
	int arr[] = { PrintArg(args)... }; //列表初始化
	cout << endl;
}

emplace插入

C++11标准给STL中的容器增加emplace版本的插入接口,比如list容器的push_front、push_back和insert函数,都增加了对应的emplace_front、emplace_back和emplace函数。如下:
在这里插入图片描述

emplace:接口的意义:

  • emplace插入和普通的插入虽然都支持插入左值和右值,但是emplace还可以插入参数包,但是他不可以使用列表初始化,这是他和其他插入函数的区别。
  • 使用参数包可以有效的解决减少一次拷贝
  • emplace系列接口真正高效的情况是传入参数包的时候,直接通过参数包构造出对象,避免了中途的一次拷贝。

例如:

namespace test
{
	class string
	{
	public:
		string(const char* str)
			:_size(strlen(str))
			, _capacity(_size)
			,_str(new char[_size + 1])
		{
			strcpy(_str, str);
			cout << "string(const char* str) -- 构造函数" << endl;
		}

		//交换两个对象的数据
		void swap(string& s)
		{
			//调用库里的swap
			::swap(_str, s._str); //交换两个对象的C字符串
			::swap(_size, s._size); //交换两个对象的大小
			::swap(_capacity, s._capacity); //交换两个对象的容量
		}
		
		//拷贝构造函数(现代写法)
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(const string& s) -- 拷贝构造" << endl;

			string tmp(s._str); //调用构造函数,构造出一个C字符串为s._str的对象
			swap(tmp); //交换这两个对象
		}

		//移动构造
		string(string&& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(const string& s) -- 移动构造" << endl;
			swap(s); //交换这两个对象
		}

		//拷贝赋值函数(现代写法)
		string& operator=(const string& s)
		{
			cout << "string& operator=(const string& s) -- 深拷贝" << endl;

			string tmp(s); //用s拷贝构造出对象tmp
			swap(tmp); //交换这两个对象
			return *this; //返回左值(支持连续赋值)
		}
		//移动赋值
		string& operator=(string&& s)
		{
			cout << "string& operator=(string&& s) -- 移动赋值" << endl;
			swap(s);
			return *this;
		}
		//析构函数
		~string()
		{
			//delete[] _str;  //释放_str指向的空间
			_str = nullptr; //及时置空,防止非法访问
			_size = 0;      //大小置0
			_capacity = 0;  //容量置0
		}

	private:
		size_t _size;
		size_t _capacity;
		char* _str;

	};
}

int main()
{
	list<pair<int, test::string>> mylist;
	pair<int, test::string> kv(1, "one");
	mylist.emplace_back(kv);
	cout << endl;

	mylist.emplace_back(pair<int, test::string>(2, "two"));
	cout << endl;

	mylist.emplace_back(2, "two");
	cout << endl;
}

在这里插入图片描述

int main()
{
	list<pair<int, test::string>> mylist;
	pair<int, test::string> kv(1, "one");
	mylist.push_back(kv);
	cout << endl;

	mylist.push_back(pair<int, test::string>(2, "two"));
	cout << endl;

	mylist.push_back({ 2, "two" });
	cout << endl;
}

在这里插入图片描述
两者最明显的区别就是一个支持参数包传,一个支持列表初始化传,但是站在内存角度来说emplace跟省空间,emplace可以无脑使用

总结一下:

  • 传入左值对象,需要调用构造函数+拷贝构造函数。
  • 传入右值对象,需要调用构造函数+移动构造函数。
  • 传入参数包,只需要调用构造函数。

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

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

相关文章

(a)Spring注解式开发,注册组件的@Repository,@Service,@Controller,@Component使用及说明

注解扫描原理 通过反射机制获取注解 Target(value {ElementType.TYPE})// 设置Component注解可以出现的位置&#xff0c;以上代表表示Component注解只能用在类和接口上 Retention(value RetentionPolicy.RUNTIME)// 设置Component注解的保持性策略&#xff0c;以上代表Comp…

社区团购美团和多多买菜小程序购物车

概述 微信小程序购物车列表demo 详细 需求 显示食物名称、价格、数量。 点击相应商品增加按钮,购买数量增加1,点击食物减少按钮,购买数量减一 显示购买总数和总金额 查看当前购买的商品 效果图(数据来自本地模拟) 目录结构 实现过程 主要wxml <view classfoods>…

工具篇 | H2数据库的使用和入门

引言 1.1 H2数据库概述 1.1.1 定义和特点 H2数据库是一款以 Java编写的轻量级关系型数据库。由于其小巧、灵活并且易于集成&#xff0c;H2经常被用作开发和测试环境中的便利数据库解决方案。除此之外&#xff0c;H2也适合作为生产环境中的嵌入式数据库。它不仅支持标准的SQL…

饮料生产线Modbus协议转换网关的应用介绍

在饮料生产线设备数据采集和控制系统中&#xff0c;MODBUS网关是一种非常重要的设备。它可以将不同设备之间的通讯协议转换为统一的MODBUS协议&#xff0c;从而实现数据采集和指令下达。在本文中&#xff0c;我们将介绍如何使用MODBUS网关采集饮料生产线设备数据并下达指令。 在…

知识库搭建保姆级教程,如何从0到1完成知识库搭建

在这个信息爆炸的时代&#xff0c;如何获取、整理和应用知识成为了我们个体价值和企业核心竞争力打造的重要表现&#xff0c;搭建一个高效的知识库可以提升我们企业的竞争力&#xff0c;必要时还能快速切换赛道&#xff0c;开展一个新的领域。 今天我们将结合HelpLook 来与你一…

基于C++实现的3D野外赛车驾驶游戏源码+项目文档+汇报PPT

项目介绍&#xff1a;本项目实现了一个户外场景下的赛车游戏&#xff0c;可以通过键盘控制赛车的移动&#xff0c;视角为第二人称视角。场景中有汽车&#xff0c;建筑&#xff0c;道路&#xff0c;天空等物体&#xff0c;拥有光照和阴影的效果。通过粒子系统模拟尾气效果&#…

Kubernetes组件和架构简介

目录 一.概念简介 1.含义&#xff1a; 2.主要功能&#xff1a; 3.相关概念&#xff1a; 二.组件和架构介绍 1.master&#xff1a;集群的控制平面&#xff0c;管理集群 2.node&#xff1a;集群的数据平面&#xff0c;为容器提供工作环境 3.kubernetes简单架构图解 一.概…

Windows迁移文件的快速方法

文章目录 1.简单比较2.传输方法介绍&#xff1a;有线&#xff08;直连网络&#xff09;3.传输方法介绍&#xff1a;无线热点传输4. 共享文件夹的设置5.挂载共享文件夹 1.简单比较 方法传输速度有线传输接近900Mb无线热点传输接近500MbU盘传输基本上不超过100Mb 2.传输方法介绍…

小程序-uniapp:URL Link / 适用于在移动端 从短信、邮件、微信外网页 等场景打开小程序任意页面

一、背景介绍 小程序URL Scheme、URL Link是微信小程序后台生成的一种地址&#xff0c;适用于从短信、邮件、微信外网页 等场景打开小程序任意页面。所以&#xff0c;适用性极强。可与微信扫码携带参数跳转到小程序指定页面技术互补 若在微信外打开&#xff0c;用户可以在浏览…

【C++】C++ 类中的 this 指针用法 ③ ( 全局函数 与 成员函数 相互转化 | 有参构造函数设置默认参数值 | 返回匿名对象与返回引用 )

文章目录 一、全局函数 与 成员函数 相互转化1、成员函数转为全局函数 - 多了一个参数2、全局函数转为成员函数 - 通过 this 指针隐藏操作数 二、有参构造函数设置默认参数值三、返回匿名对象与返回引用四、完整代码示例 一、全局函数 与 成员函数 相互转化 1、成员函数转为全局…

2023-Chrome插件推荐

Chrome插件推荐 一键管理扩展 链接 https://chromewebstore.google.com/detail/lboblnfejcmcaplhnbkkfcienhlhpnni 介绍 一键开启、禁用Chrome插件。 Checker Plus for Gmail™ 链接 https://jasonsavard.com/zh-CN/Checker-Plus-for-Gmail https://chromewebstore.goo…

ElementUI之首页导航与左侧菜单

目录 一、Mock 1.1 什么是Mock.js 1.2 安装与配置 1.2.1 安装mock.js 1.2.2 引入mock.js 1.3 mock.js使用 1.3.1 定义测试数据文件 1.3.2 mock拦截Ajax请求 1.3.3 界面代码优化 二、总线 2.1 定义 2.2 类型分类 2.3 前期准备 2.4 配置组件与路由关系 2.4.1 配置…

计算机毕业设计 基于微信小程序的校园商铺系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

2023 “华为杯” 中国研究生数学建模竞赛(A题)深度剖析|数学建模完整代码+建模过程全解全析

华为杯数学建模A题 当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2021年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题。 让我们一起看看研赛的A题呀&#xff01; …

络安全开发和音视频开发哪个方向更有前景?

网络安全开发和音视频开发哪个方向更有前景&#xff1f; 随着互联网和移动互联网的飞速发展&#xff0c;音视频应用已经成为人们日常生活和工作中不可或缺的一部分。 从视频会议、在线教育、直播、短视频到游戏&#xff0c;音视频技术的应用场景越来越广泛&#xff0c;市场需求…

面试必杀技:Jmeter性能测试攻略大全(第二弹)

1. JMeter介绍与安装 JMeter介绍 JMeter是Apache组织开发的基于Java的压力测试工具。具有开源免费、框架灵活、多平台支持等优势。除了压力测试外&#xff0c;JMeter也可以应用的接口测试上。JMeter下载、安装及启动 下载&#xff1a; 访问JMeter官网&#xff1a;https://j…

机器学习——seaborn实用画图方法简介

0、seaborn简介: 前言:下面的总结只是介绍seaborn有哪些方法和属性,至于具体使用,通过下面给出的名称稍作查找即可。重点应该关注本文介绍的seaborn的使用方法seaborn与机器学习的关系: 知识图谱 0.1、了解即可的知识: seaborn:在matplotlib的基础上画一些更好看的图,在…

快速排序与代码

快速排序&#xff08;Quicksort&#xff09;是一种常用的排序算法&#xff0c;它基于分治的思想。 时间复杂度&#xff1a;O&#xff08;nlogn&#xff09; 空间复杂度&#xff1a;O&#xff08;logn&#xff09; 快速排序的基本思想如下&#xff1a; 选择一个元素作为基准&a…

Android Jetpack组件架构 :LiveData的使用和原理

Android Jetpack组件架构&#xff1a; LiveDate的使用和原理 导言 继Lifecycle组件之后我们接下来要介绍的就是LiveDate组件&#xff0c;所谓LiveDate字面意思上就是有声明的数据&#xff0c;当数据有改动时该组件可以感知到这个操作并将该事件通知到其观察者&#xff0c;这样…

STM32单片机入门学习(四)-蜂鸣器

蜂鸣器接线 低平蜂鸣器&#xff0c;低电平发声&#xff0c;高电平不发声&#xff0c; 三个排针&#xff0c;VCC接3.3v&#xff0c;GND接地&#xff0c;I/O接A0口&#xff0c;如图&#xff1a; 蜂鸣器代码&#xff1a;响一秒停半秒 #include "stm32f10x.h" #includ…