【C++11(二)】lambda表达式和可变参数模板

news2025/1/11 16:45:20

在这里插入图片描述

一、可变参数模板

C++11的新特性可变参数模板
能够让您创建可以接受
可变参数的函数模板和类模板

// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}

参数包是不支持下标解析的
在这里插入图片描述

1.1 递归函数方式展开参数包

// 递归终止函数
void ShowList()
{
 	cout << endl;
}

// 展开函数
// 增加一个模板参数让编译器自己去解析这个参数包里的东西
template <class T, class ...Args>
void ShowList(const T& value, Args... args)
{
 	cout << value <<" ";
 	ShowList(args...); // 如果是0个参数的参数包就会调用void ShowList()
 	// 如果参数包接收的char类型的参数,会再去调匹配的ShowList函数
 	// 调不到就只能调自己,根据模板推导类型
 	// 打印完,参数包再往下传,0个参数就调用void ShowList()
 	// void ShowList()可以认为是递归终止函数
}

int main()
{
	ShowList(); // 编译器会找最匹配的,调void ShowList()
 	ShowList(1); // 1传给value,后面的参数包就没有了,参数包代表0-n个参数
	ShowList(1, 'A'); // 1传给value,'A'传给参数包
 	ShowList(1, 'A', std::string("sort"));
 return 0;
}

二、lambda表达式

C++98中
对一个数据集合中的元素排序
可以使用std::sort

如果待排序元素为自定义类型
需要自己写仿函数
如果每次按自定义类型不同的成员变量
进行排序就要写多个仿函数,十分不方便
因此,C++11语法中的Lambda表达式
便是解决此类问题

lambda表达式

int main()
{
 vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2, 3 }, { "菠萝", 1.5, 4 } };
 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; });
}

可以发现lambda表达式是一个匿名函数

1.1 lambda表达式语法

书写格式:
[捕捉列表] (参数列表) mutable ->return-type { 函数体 }

  • 捕捉列表:该列表总出现lambda函数开始位置
    编译器根据[]判断接下来的代码是否为lambda函数
    捕捉列表能够捕捉上下文中的变量供lambda函数使用
  • 参数列表:与普通函数的参数列表一致
    如果不需要参数传递,则可以连同()一起省略
  • mutable:默认情况lambda函数总是const函数,mutable可以取消其常量性
    使用该修饰符参数列表不可省略(即使参数为空)
  • ->return-type:返回值类型
    用追踪返回类型形式声明函数的返回值类型
    无返回值可略,有返回值也可略,由编译器推导
  • { 函数体 }:该函数体内除了可使用其参数外
    还可以使用所有捕获到的变量

用lambda表达式实现add

auto add = [](int x, int y)->int { return x + y; };

// cout << [](int x, int y)->int { return x + y; }(1, 2) << endl; // 这样写比较抽象
cout << add(1, 2) << endl;

auto add2 = [](int x, int y)
{ 
	return x + y; 
};
cout << add2(3, 2) << endl;

[] {}; // 最简单的lambda,捕捉列表和函数体是一定不能省略的

用lambda对自定义类型比较大小

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

int main()
{
	vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2, 3 }, { "菠萝", 1.5, 4 } };
    
	// 用lambda对自定义类型比较大小
	// <
	auto priceLess = [](const Goods& g1, const Goods& g2)->bool { return g1._price < g2._price; };
	sort(v.begin(), v.end(), priceLess);
	// >
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool { 
		return g1._price > g2._price; });

	// 比较评价
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool {
		return g1._evaluate > g2._evaluate; });

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

	return 0;
}

捕捉变量

int main()
{
	int x = 1, y = 2;
	auto swap1 = [](int& rx, int& ry)
	{
		// 只能用当前作用域的变量
		int tmp = rx;
		rx = ry;
		ry = tmp;
	};
	swap1(x, y);
	cout << x << " " << y << endl;

	// 还可以换一种写法
	// 想用外面的变量,则可以利用捕捉列表进行捕捉(捕捉过来的对象是外面对象的拷贝)
	/*
	// 传值捕捉
	auto swap2 = [x, y]() mutable // 捕捉多个值用逗号分割即可;直接给值叫做传值捕捉,传值捕捉无法修改,加上mutable(异变)就可以修改
	{
		int tmp = x;
		x = y;
		y = tmp;
	};
	swap2();
	cout << x << " " << y << endl;
	*/
	
	// mutable用得比较少,建议
	// 引用捕捉
	auto swap2 = [&x, &y]()
		{
			int tmp = x;
			x = y;
			y = tmp;
		};
	swap2();
	cout << x << " " << y << endl;
	// 还可以混合捕捉,x引用捕捉,y传值捕捉

	// 全部引用捕捉
	auto func1 = [&]()
		{
			// ...
		};

	// 全部传值捕捉
	auto func2 = [=]()
		{
			// ...
		};

	// 全部引用捕捉,x传值捕捉
	auto func3 = [&, x]()
		{
			// ...
		};

	return 0;
}

1.2 函数对象与lambda表达式

函数对象,又称为仿函数
即可像函数一样使用的对象
(在类中重载了operator()运算符的类对象)

class Rate
{
public:
 Rate(double rate): _rate(rate)
 {}
 double operator()(double money, int year)
 { return money * _rate * year;}
private:
 double _rate;
};
int main()
{
// 函数对象
 double rate = 0.49;
 Rate r1(rate);
 r1(10000, 2);
// lamber
 auto r2 = [=](double monty, int year)->double{return monty*rate*year; 
};
 r2(10000, 2);
 return 0;
}

lambda表达式大小为1个字节
在编译器角度是没有lambda
定义一个lambda
编译器自动生成一个仿函数对象的类型
在该类中重载了operator()
该类是一个空类,空类没给成员变量就是一个字节

函数对象将rate作为其成员变量
在定义对象时给出初始值即可
lambda表达式通过捕获列表可以
直接将该变量捕获到

三、包装器

C++中的function本质是一个类模板
也是一个包装器

int f(int a, int b)
{
	return a + b;
}

struct Functor
{
public:
	int operator()(int a, int b)
	{
		return a + b;
	}
};

int main()
{
	// int(*pf1)(int, int) = f; // 不是常规的指针类型,写法复杂

	// 假设要求要声明一个统一的类型
	// map<string, > // 这里要声明可调用类型,f和Functor调用起来都是一样的,但类型不一样,一个是函数指针一个是类
	// 这时候就没法声明,而包装器就可以统一封装出可调用类型
	function<int(int, int)> f1 = f; // 返回值加参数包,参数包就是把实际要的类型写上
	function<int(int, int)> f2 = Functor(); // Function可以对函数指针和仿函数对象进行包装
	function<int(int, int)> f3 = [](int a, int b) { return a + b; };

	cout << f1(1, 2) << endl; // 包装以后两个类型的对象是一样的
	cout << f2(2, 2) << endl;
	cout << f3(3, 3) << endl;

	map<string, function<int(int, int)>> opFuncMap; 
	opFuncMap["函数指针"] = f;
	opFuncMap["仿函数"] = Functor();
	opFuncMap["lambda"] = [](int a, int b) { return a + b; };

	// 包装器的作用:更好的控制可调用对象的类型

	return 0;
}

静态成员和非静态成员的包装

class Plus
{
public:
	Plus(int rate)
		: _rate(rate)
	{}

	static int plusi(int a, int b)
	{
		return a + b;
	}
	double plusd(double a, double b)
	{
		return (a + b) * _rate;
	}
private:
	int _rate = 2;
};

int main()
{
	function<int(int, int)> f1 = Plus::plusi; // 包装静态成员函数正常包装就可以

	// 非静态成员函数,就不能这样直接包装
	// function<double(double, double)> f2 = Plus::plusd;
	// error C3867: “Plus::plusd”: 非标准语法;请使用 "&" 来创建指向成员的指针
	// 普通成员函数名代表函数指针.静态成员也一样,指定类域就可取到这个函数指针
	
	// 非静态成员需要加一个&
	// 非静态传参还有一个this指针需要传参
	// error C2440 : “初始化”: 无法从“double(__cdecl Plus::*)(double, double)”转换为“std::function<double(double, double)>”
	function<double(Plus, double, double)> f2 = &Plus::plusd; 
	// 也可以传Plus*,左值能被取地址,右值不行,匿名对象是右值不能取地址,就不能用匿名对象
	// 传指针也可以,传对象也可以,因为这不是直接去掉用plusd这个函数,我是一个包装器,f1是直接调用Plusi
	// f2是用对象去掉用Plusd
	// 当Plusd是指针,就用指针取调用Plusd
	// 如果是对象就用对象调用Plusd 

	cout << f1(1, 2) << endl;
	cout << f2(Plus(2), 20, 20) << endl; // 第一个正常调用,第二个需要加一个匿名对象;需要写一个构造函数,也可以不写,Plus就不能传参
	// 也可以不用匿名对象
	Plus p1(3);
	cout << f2(p1, 20, 20) << endl;

	return 0;
}

bind

调整参数顺序

void Print(int a, int b)
{
	cout << a << " ";
	cout << b << endl;
}

int main()
{
	Print(10, 20); 
	// auto RPrint =  bind(Print, placeholders::_2, placeholders::_1); // 第一个参数传可调用对象,_1是一个占位符也是第一个参数,-2是第二个参数以此类推,默认是拿不到的,它放在placeholders命名空间里面
	function<void(int, int)> RPrint = bind(Print, placeholders::_2, placeholders::_1);
	// 两种写法都可以推荐用auto

	RPrint(10, 20);

	return 0;
}

调整参数个数

class Sub
{
public:
	Sub(int rate)
		: _rate(rate)
	{}

	int func(int a, int b)
	{
		return (a - b) * _rate;
	}
private:
	int _rate;
};

int main()
{
	function<int(Sub, int, int)> fSub = &Sub::func;
	cout << fSub(Sub(3), 10, 20) << endl;
	
	function<int(int, int)> fSub1 = bind(&Sub::func, Sub(3), placeholders::_1, placeholders::_2);
	cout << fSub1(10, 20) << endl;
	// 把隐藏this指针绑死就只用传两个参数

	// 把第二个参数绑死
	function<int(Sub, int)> fSub2 = bind(&Sub::func, placeholders::_1, 100, placeholders::_2);
	// 第二个参数绑死了,第三个参数是_2,还是按顺序挨着走 
	cout << fSub2(Sub(3), 20) << endl;

}

在这里插入图片描述
本篇博客完,感谢阅读🌹
如有错误之处可评论指出
博主会耐心听取每条意见

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

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

相关文章

笔记101:OSQP求解器的底层算法 -- ADMM算法

前言1&#xff1a;这篇博客仅限于介绍拉格朗日乘子法&#xff0c;KKT条件&#xff0c;ALM算法&#xff0c;ADMM算法等最优化方法的使用以及简版代码实现&#xff0c;但不会涉及具体的数学推导&#xff1b;不过在下面我会给出具体数学推导的相关文章和截图&#xff0c;供学有余力…

Elasticsearch:使用 Llamaindex 的 RAG 与 Elastic 和 Llama3

这篇文章是对之前的文章 “使用 Llama 3 开源和 Elastic 构建 RAG” 的一个补充。我们可以在本地部署 Elasticsearch&#xff0c;并进行展示。我们将一步一步地来进行配置并展示。你还可以参考我之前的另外一篇文章 “Elasticsearch&#xff1a;使用在本地计算机上运行的 LLM 以…

在线epub阅读器epub;在线图书阅读器;专门为epub定制的阅读器;免费在线电子图书epub阅读器

背景&#xff1a;不记得某时某刻了&#xff0c;就是当时想要使用电脑阅读epub图书&#xff0c;也找了好些个在线epub阅读器&#xff0c;但总有一些不如意的地方&#xff0c;如某些功能需要会员之类的&#xff0c;突发临想的就想到自己开发一个&#xff0c;就此&#xff0c;一个…

大模型RAG技术:构建高效、可信赖的知识检索系统

前言 LLM 问题 幻觉&#xff1a;在没有答案的情况下提供虚假信息。 过时&#xff1a;当用户需要特定的当前响应时&#xff0c;提供过时或通用的信息。 来源&#xff1a;从非权威来源创建响应。由于术语混淆&#xff0c;不同的培训来源使用相同的术语来谈论不同的事情&#…

C# Onnx Yolov8-OBB 旋转目标检测 行驶证副页条码+编号 检测,后续裁剪出图片并摆正显示

C# Onnx Yolov8-OBB 旋转目标检测 行驶证副页条码编号 检测&#xff0c;后续裁剪出图片并摆正显示 目录 效果 模型信息 项目 代码 下载 效果 模型信息 Model Properties ------------------------- date&#xff1a;2024-06-25T10:59:15.206586 description&#xff1a;…

第一课:SSH协议、SSHD守护进程、Openssh软件包

第一节课 6月12日 ssh协议 关键问题 一、ssh、sshd、openssh的概念和区别&#xff1f; 二、ssh是基于什么架构&#xff1f;B/S还是C/S&#xff1f; 三、用户远程连接服务器经历哪些过程&#xff1f; 四、如何查看openssh软件包是否安装&#xff1f; 五、rpm和yum的区别&#xf…

node带参数命令

不带参数命令示例&#xff1a; node /www/wwwroot/server 带参数命令示例&#xff1a; node /www/wwwroot/server arg1 arg2 arg3 在启动页进行参数处理&#xff1a; // 获取启动参数(除去前2个默认参数&#xff0c;示例&#xff1a;node /www/wwwroot/server arg1 arg2 …

SAP ABAP 之容器

文章目录 前言一、案例介绍/笔者需求二、自定义容器 a.实例化对象 b.自定义容器效果演示 c.Copy Code 三、自适应容器 a.常用 必须 参数理解 b.METRIC 度量单位 c.RATIO 百分比尺寸 d.STYLE 容器…

WMV 视频格式怎么转换?WMV 视频为什么不流行了?

目前有越来越多的视频格式类型&#xff0c;如常见的 MP4、FLV、AVI 等等&#xff0c;而技术的演变也逐渐让一些常见的视频格式变的越来越少了。 今天我们一起来聊下 WMV 这个视频格式&#xff0c;让我们看看它的发展以及为什么现在越来越少人使用了。 什么是 WMV 视频格式&…

微信营销自动化(朋友圈自动点赞工具):UIAutomation的解决方案

文章不用看, 是AI生成的, 请直接查看下载地址 http://www.aisisoft.top . 微信朋友圈自动点赞工具, 自动群发工具 在当今的数字化营销领域&#xff0c;自动化工具成为了提升工作效率、增强客户互动的关键。本文将详细介绍一款基于UIAutomation框架与Python语言构建的微信营销自…

数据容器(四)

目录 一、dict&#xff08;字典、映射&#xff09; 1.字典的定义 2.字典数据的获取 3.字典的嵌套 一、dict&#xff08;字典、映射&#xff09; 1.字典的定义 使用{}&#xff0c;不过存储的元素是一个个的&#xff1a;键值对。 2.字典数据的获取 字典同集合一样&#xff…

PointCloudLib-滤波模块(Filtering)-使用统计异常值移除过滤器移除异常值

在本教程中,我们将学习如何消除噪声测量值,例如异常值, 使用统计分析技术的点云数据集。 背景 激光扫描通常会生成不同点密度的点云数据集。 此外,测量误差会导致稀疏异常值,从而破坏 结果更多。这使得本地点云的估计变得复杂 表面法线或曲率变化等特征,导致 错误的值,…

【WEB】关于react的WEB应用中使用React Developer Tools便捷快速查看元素数据

1、往扩展工具中添加React Developer Tools的扩展包 2、检查是否生效&#xff0c;如下图&#xff1a; 可以看到右上角多出来一个Components的tab选项&#xff0c;就是成功了

转运机器人:智能物流的得力助手

在物流行业&#xff0c;转运机器人已经成为提高转运效率、降低成本的重要工具。而富唯智能转运机器人凭借其出色的性能和智能化的设计&#xff0c;成为了众多企业的得力助手。 富唯智能转运机器人采用了先进的AMR控制系统&#xff0c;可以一体化控制移动机器人并实现与产线设备…

美国众议院通过ENFORCE ACT草案:AI领域的潜在冷战?

近日&#xff0c;美国众议院通过了“增强关键出口海外限制国家框架法案”&#xff08;ENFORCE ACT&#xff09;&#xff0c;该法案旨在限制AI/ML技术和人才向中国的流动。这一举动引发了广泛讨论和担忧&#xff0c;许多人认为这将对在美从事AI相关工作的中国人造成重大影响。本…

【力扣】重排链表

&#x1f525;博客主页&#xff1a; 我要成为C领域大神 &#x1f3a5;系列专栏&#xff1a;【C核心编程】 【计算机网络】 【Linux编程】 【操作系统】 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 本博客致力于分享知识&#xff0c;欢迎大家共同学习和交流。 给定一个单链表…

传媒行业采购堡垒机的必要性你知道吗?

随着互联网的快速发展&#xff0c;传媒行业也是发展速度。特别是近年来&#xff0c;自媒体行业的火热&#xff0c;如何保障网络安全&#xff0c;如何保障大家信息安全至关重要。虽然国家严格要求执行等保政策&#xff0c;但大家对于为什么传媒行业要采购堡垒机不是很了解。你知…

JDK16特性

JDK16特性 一、JAVA16概述 2021年3月16日正式发布,一共更新了17JEP https://openjdk.java.net/projects/jdk/16/ 二、语法层面变化 1.JEP 397:密封类(第二次预览) sealed class 第二次预览通过密封的类和接口来增强Java编程语言,这是新的预览特性,用于限制超类的使用密封…

Nuxt 3组件开发与管理

title: Nuxt 3组件开发与管理 date: 2024/6/20 updated: 2024/6/20 author: cmdragon excerpt: 摘要&#xff1a;本文深入探讨了Nuxt 3的组件开发与管理&#xff0c;从基础概念、安装配置、目录结构、组件分类与开发实践、生命周期与优化&#xff0c;到测试与维护策略。详细…

小程序注册

【 一 】小程序注册 微信公众平台 https://mp.weixin.qq.com/ https://mp.weixin.qq.com/注册 邮箱激活 小程序账户注册 微信小程序配置 微信小程序开发流程 添加项目成员 【 二 】云服务 lass 基础设施服务&#xff08;组装机&#xff09; 你买了一大堆的电脑配件&#x…