C++11详解(三) -- 可变参数模版和lambda

news2025/3/11 8:29:19

文章目录

  • 1.可变模版参数
    • 1.1 基本语法及其原理
    • 1.2 包扩展
    • 1.3 empalce系列接口
      • 1.3.1 push_back和emplace_back
      • 1.3.2 emplace_back在list中的使用(模拟实现)
  • 2. lambda
    • 2.1 lambda表达式语法

1.可变模版参数

1.1 基本语法及其原理

1. C++11支持可变参数模版,也就是说支持可变数量参数的函数模版和类模版,可变数目的参数为参数包,存在两种参数包:模板参数包,表示零或多个模板参数;函数参数包:表示零或多个函数参数。
2. 用省略号来指出一个模板参数或函数参数的表示一个包,在模板参数列表中,class…或typename…指出接下来的参数表示零或多个类型列表;
在函数参数列表中,类型名后面跟…指出接下来表示零或多个形参对象列表;
函数参数包可以用左值引用或右值引用表示,跟前面普通模板一样,每个参数实例化时遵循引用折叠规则。
3. 可变参数模板的原理跟模板类似,本质还是去实例化对应类型和个数的多个函数。
4. 可变模版参数:参数类型可变,参数个数可变

…Args:模版参数包
…args:函数参数包
在这里插入图片描述

// 0-N个参数
// ...:表示多个参数
template<class ...Args>
void Print(Args&& ... args)
{
    // sizeof...是一个新的运算符
	// sizeof... 计算参数包里面有几个参数
	cout << sizeof...(args) << endl;
}

int main()
{
	Print(); // 0个参数
	Print(1);// 1个参数
	Print(1, 2.2);// 2个参数

	return 0;
}

在这里插入图片描述

  • 可变模版参数:本质上是模版的模版,可变模版参数实例化出对应的各个模版,这些模版再实例化出对应的类或者是函数

在这里插入图片描述

  • 总结:模版,一个函数模版可以实例化出多个不同类型参数的函数,可变参数模版,一个可变参数模版函数可以实例化出多个不同参数个数的模版函数
  • 主要就是一个可变参数模版实例化出多个函数模版,函数模版再实例化多个函数

1.2 包扩展

1. 包扩展:解析出参数包的内容
2. 编译时递归包括展,其实也不是递归,因为每次都生成不同的函数重载,只是每次用自己这个函数进行传参(包扩展)

参数包的第一种扩展方式(编译时递归):

// 打印参数包内容
template <class ...Args>
void Print(Args... args)
{
  // 可变参数模板是编译时解析,不是运行时解析
 // 下面是运行获取和解析,所以不支持这样用
 //  cout << sizeof...(args) << endl;
    for (size_t i = 0; i < sizeof...(args); i++)
    {
	   cout << args[i] << " ";
    }

     cout << endl;
}

// 这是编译时的逻辑,不是运行时逻辑
void ShowList()
{
	// 编译器递归终止的条件,参数包是0个时,直接匹配这个函数
	cout << endl;
}

template<class T,class ...Args>
void ShowList(T x, Args ...args)
{
    // 运行时
    /*if (sizeof...(args) == 0)
	return;*/
	
	cout << x << " ";
	// args是N个参数的参数包,一个参数传给x,剩下的N-1个参数
	// 传给args,继续往下递归
	ShowList(args...);
}

// 编译时递归推导解析参数
template<class ...Args>
void Print(Args ... args)
{
	ShowList(args...);
}

int main()
{
	Print();
	Print(1);
	Print(1, string("xxxxxx"));
	Print(1,string("xxx"),2.2);

	return 0;
}

模版写给编译器,让编译器生成对应的包括展
在这里插入图片描述
第二种扩展方式(通过函数调用):

// 下面两个GetArg都可以用
// 可以随便返回任何数
template<class T>
int GetArg(const T& x)
{
	cout << x << " ";
	return 0;
}

//template <class T>
//const T& GetArg(const T& x)
//{
//	cout << x << " ";
//	return x;
//}

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

template <class ...Args>
void Print(Args... args)
{
	// 注意GetArg必须返回或者到的对象,这样才能组成参数包给Arguments
	Arguments(GetArg(args)...);
	有几个参数调用几次Arguments
}

// 实际上就是下面这段
// 本质可以理解为编译器编译时,包的扩展模式
// 将上⾯的函数模板扩展实例化为下⾯的函数
//void Print(int x, string y, double z)
//{
//   Arguments(GetArg(x), GetArg(y), GetArg(z));
//}

int main()
{
	Print(1, string("xxxxx"), 2.2);
	return 0;
}

1.3 empalce系列接口

1.3.1 push_back和emplace_back

  1. 传左值:都会走拷贝构造
    在这里插入图片描述
  2. 传右值:都走移动构造
    在这里插入图片描述
  3. 对于深拷贝
    直接传参,push_back,类模版实例化出string,会构造临时对象+移动构造
    emplace_back,会直接用const char* 构造
    所以emplace_back会稍微快一点
    对于浅拷贝
    比如Date,字节大小不大,push_back变为构造 + 拷贝构造,emplace_back还是直接构造
    在这里插入图片描述
    多参数的:
  4. 对于pair键值对,It1.emplace_back({“苹果”,1})是不支持的,因为emplace_back支持传多个参数类型,而不知道你传的是键值对pair
    在这里插入图片描述

1.3.2 emplace_back在list中的使用(模拟实现)

其实并不都是要像Print中解析包扩展,这样往下传即可

// 初始化列表
list_node() = default;

template <class... Args>
list_node(Args&&... args)
		: _next(nullptr)
		, _prev(nullptr)
		, _data(std::forward<Args>(args)...)
	    {}

// emplace_back()
template <class... Args>
void emplace_back(Args&&... args)
{
	insert(end(), std::forward<Args>(args)...);
}

// insert()
template <class... Args>
iterator insert(iterator pos, Args&&... args)
{
		Node* cur = pos._node;
		Node* newnode = new Node(std::forward<Args>(args)...);
		Node* prev = cur->_prev;

		// prev newnode cur
		prev->_next = newnode;
		newnode->_prev = prev;
		newnode->_next = cur;
		cur->_prev = newnode;
		return iterator(newnode);
}

2. lambda

2.1 lambda表达式语法

1. lambda 表达式本质是一个匿名函数对象,跟普通函数不同的是他可以定义在函数内部,也可以写在全局,lambda 表达式语法使用层而言没有类型,所以我们一般是用auto或者模板参数定义的对象去接收 lambda 对象。
2.lambda表达式的格式:
[捕捉列表](参数列表)->返回值类型{函数体}
3. lambda可以传给模版的参数,也可以传给auto,让auto自动推导

#include<algorithm>

struct Goods
{
	string _name; // 名字
	double _price; // 价格
	int _evaluate; // 评价
	// ...
	Goods(const char* str, double price, int evaluate)
		:_name(str)
		, _price(price)
		, _evaluate(evaluate)
	{}
};

// 仿函数,价格升序
struct Compare1
{
	bool operator()(const Goods& gl, const Goods& gr)
	{
		return gl._price < gr._price;
	}
};

// 价格降序
struct Compare2
{
	bool operator()(const Goods& gl, const Goods& gr)
	{
		return gl._price > gr._price;
	}
};

int main()
{
	vector<Goods> v = { { "苹果", 2.1, 5 }, { "⾹蕉", 3, 4 }, { "橙⼦", 2.2, 3}, { "菠萝", 1.5, 4 } };
	// 类似这样的场景,我们实现仿函数对象或者函数指针⽀持商品中
	// 不同项的⽐较,相对还是⽐较⿇烦的,因为只能比较一项,那么这⾥lambda就很好⽤了

	// 写法一
	// sort(v.begin(), v.end(), Compare1());
	// sort(v.begin(), v.end(), Compare2());

	// 写法二
	// 用 lambda就可以用匿名函数对象比较多种数据,不用写专门的仿函数单独比较一项了
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) 
		{return g1._price < g2._price;});

	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) 
		{return g1._price > g2._price;});

	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) 
		{return g1._evaluate < g2._evaluate;});

	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) 
	{return g1._evaluate > g2._evaluate;});
	// [](const Goods& g1, const Goods& g2) {return g1._evaluate > g2._evaluate; }
	// 匿名的函数对象

	return 0;
}

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

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

相关文章

网站打开提示不安全

当网站打开时显示“不安全”提示&#xff08;通常表现为浏览器地址栏中出现“不安全”字样或红色警告图标&#xff09;&#xff0c;这意味着网站未使用有效的SSL证书或HTTPS协议&#xff0c;导致浏览器认为连接不安全。以下是解决这一问题的详细步骤&#xff1a; 一. 原因分析 …

OpenCV:特征检测总结

目录 一、什么是特征检测&#xff1f; 二、OpenCV 中的常见特征检测方法 1. Harris 角点检测 2. Shi-Tomasi 角点检测 3. Canny 边缘检测 4. SIFT&#xff08;尺度不变特征变换&#xff09; 5. ORB 三、特征检测的应用场景 1. 图像匹配 2. 运动检测 3. 自动驾驶 4.…

python学opencv|读取图像(五十七)使用cv2.bilateralFilter()函数实现图像像素双边滤波处理

【1】引言 前序学习过程中&#xff0c;已经掌握了对图像的基本滤波操作技巧&#xff0c;具体的图像滤波方式包括均值滤波、中值滤波和高斯滤波&#xff0c;相关文章链接有&#xff1a; python学opencv|读取图像&#xff08;五十四&#xff09;使用cv2.blur()函数实现图像像素…

【SQL技术】不同数据库引擎 SQL 优化方案剖析

一、引言 在数据处理和分析的世界里&#xff0c;SQL 是不可或缺的工具。不同的数据库系统&#xff0c;如 MySQL、PostgreSQL&#xff08;PG&#xff09;、Doris 和 Hive&#xff0c;在架构和性能特点上存在差异&#xff0c;因此针对它们的 SQL 优化策略也各有不同。这些数据库中…

链式结构二叉树(递归暴力美学)

文章目录 1. 链式结构二叉树1.1 二叉树创建 2. 前中后序遍历2.1 遍历规则2.2 代码实现图文理解 3. 结点个数以及高度等二叉树结点个数正确做法&#xff1a; 4. 层序遍历5. 判断是否完全二叉树 1. 链式结构二叉树 完成了顺序结构二叉树的代码实现&#xff0c;可以知道其底层结构…

技术文档管理最佳实践:高效、专业、可持续

文章目录 技术文档管理最佳实践&#xff1a;高效、专业、可持续1. 技术文档的核心价值1.1 降低知识流失风险1.2 提升开发效率1.3 增强团队协作1.4 规范技术资产管理 2. 技术文档分类与规范2.1 代码相关文档2.2 过程与运维文档2.3 知识与培训文档 3. 工具选型&#xff1a;自动化…

56. Uboot移植实验

一、NXP官方Uboot编译与测试 1、将NXP提供的uboot拷贝到ubuntu中。 一个开发板也好运行uboot&#xff0c;DDR或者叫DRAM&#xff0c;串口&#xff0c;SD、EMMC、NAND。板子能工作。 测似结果&#xff1a; 1、uboot能正常启动 2、LCD驱动要根据所使用的屏幕修改。 3、NET初始…

AI大模型:本地部署deepseek

一、安装lmstudio 1、下载网站&#xff1a; LM Studio - Discover, download, and run local LLMs 2、直接安装即可&#xff0c;记住安装的路径 二、下载deepseek模型 2.1、下载的流程 1、下载网站 https://huggingface.co/models 2、在搜索框输入&#xff1a;deepseek …

RK3588平台开发系列讲解(DMA篇)DMA engine使用

文章目录 一、DMA 使用步骤二、DMA接口2.1、DMA 通道管理相关接口2.2、DMA 描述符相关接口2.3、DMA 启动与控制接口2.4、DMA 状态检查接口2.5、 DMA 缓存管理接口2.6、DMA 中断与同步机制沉淀、分享、成长,让自己和他人都能有所收获!😄 Linux 内核的 DMA 引擎提供了一组完整…

报名 | IEEE ICME 2025 音频编码器能力挑战赛正式开启

音频编码器是多模态大模型的重要组件&#xff0c;优秀的音频编码器在构建多模态系统中至关重要。在此背景下&#xff0c;小米集团、萨里大学、海天瑞声共同主办了 IEEE International Conference on Multimedia & Expo (ICME) 2025 Audio Encoder Capability Challenge。 …

ASP.NET Core标识框架Identity

目录 Authentication与Authorization 标识框架&#xff08;Identity&#xff09; Identity框架的使用 初始化 自定义属性 案例一&#xff1a;添加用户、角色 案例二&#xff1a;检查登录用户信息 案例三&#xff1a;实现密码的重置 步骤 Authentication与Authorizatio…

PFAS(全氟烷基和多氟烷基物质)测试流程详细介绍

PFAS&#xff08;全氟烷基和多氟烷基物质&#xff09;测试详细介绍 什么是PFAS&#xff1f; PFAS是(Per-and polyfluoroalkyl substances)的简称&#xff0c;中文名&#xff1a;全氟烷基和多氟烷基物质&#xff0c;是一系列合成有机氟化物的总称&#xff0c;是指至少含有一个…

宝塔面板端口转发其它端口至MySQL的3306

最近需要把服务器的MySQL服务开放给外网&#xff0c;但又希望公开给所有人。也不想用默认的3306端口。同时也不想改变MySQL的默认端口。 这时候最好的办法就是用一个不常用的端口来转发至3306上去。例如使用49306至3306&#xff0c;外网通过49306来访问&#xff0c;内网依然使用…

inquirer介绍及配合lerna在Vue中使用示例

目录 安装基本用法使用多个提示框动态选择&#xff08;动态选项&#xff09;表单式输入配合lerna在Vue中使用示例 Inquirer 是一个用于创建交互式命令行工具的 Node.js 库&#xff0c;常用于收集用户输入。它提供了多种类型的提示框&#xff0c;可以用于创建交互式应用程序&…

基于MODIS/Landsat/Sentinel/国产卫星遥感数据与DSSAT作物模型同化的作物产量估算

基于过程的作物生长模拟模型DSSAT是现代农业系统研究的有力工具&#xff0c;可以定量描述作物生长发育和产量形成过程及其与气候因子、土壤环境、品种类型和技术措施之间的关系&#xff0c;为不同条件下作物生长发育及产量预测、栽培管理、环境评价以及未来气候变化评估等提供了…

如何打开vscode系统用户全局配置的settings.json

&#x1f4cc; settings.json 的作用 settings.json 是 Visual Studio Code&#xff08;VS Code&#xff09; 的用户配置文件&#xff0c;它存储了 编辑器的个性化设置&#xff0c;包括界面布局、代码格式化、扩展插件、快捷键等&#xff0c;是用户全局配置&#xff08;影响所有…

【Uniapp-Vue3】从uniCloud中获取数据

需要先获取数据库对象&#xff1a; let db uniCloud.database(); 获取数据库中数据的方法&#xff1a; db.collection("数据表名称").get(); 所以就可以得到下面的这个模板&#xff1a; let 函数名 async () > { let res await db.collection("数据表名称…

【重生之学习C语言----杨辉三角篇】

目录 ​编辑 --------------------------------------begin---------------------------------------- 一、什么是杨辉三角&#xff1f; 二、问题分析 三、算法设计 使用二维数组存储杨辉三角&#xff1a; 递推关系&#xff1a; 格式化输出&#xff1a; 四、代码实现 完…

LabVIEW自定义测量参数怎么设置?

以下通过一个温度采集案例&#xff0c;说明在 LabVIEW 中设置自定义测量参数的具体方法&#xff1a; 案例背景 ​ 假设使用 NI USB-6009 数据采集卡 和 热电偶传感器 监测温度&#xff0c;需自定义以下参数&#xff1a; 采样率&#xff1a;1 kHz 输入量程&#xff1a;0~10 V&a…

Vim的基础命令

移动光标 H(左) J(上) K(下) L(右) $ 表示移动到光标所在行的行尾&#xff0c; ^ 表示移动到光标所在行的行首的第一个非空白字符。 0 表示移动到光标所在行的行首。 W 光标向前跳转一个单词 w光标向前跳转一个单词 B光标向后跳转一个单词 b光标向后跳转一个单词 G 移动光标到…