lambda表达式介绍

news2024/11/18 11:40:19

前言

        lambda表达式是C++11标准才支持的,有了它以后在一些地方进行使用会方便很多,尤其在一些需要仿函数的地方,lambda表达式完全可以替代它的功能。代码的可读性也会提高。

目录

1.lambda表达式

2.lambda表达式语法

3.函数对象和lambda表达式


1.lambda表达式

        在C++98中如果想要对一个数据集合进行排序,可以使用std::sort的方法 

//仿函数
template<class T>
struct Greater 
{
	bool operator()(const T& nums1,const T& nums2)
	{
		return nums1 > nums2;
	}
};
//函数
bool FunGreater(const int&nums1,const int&nums2)
{
	return nums1 > nums2;
}
void TestSort()
{
	int array[] = { 4,1,8,5,3,7,0,9,2,6 };
	std::sort(array, array + sizeof(array)/sizeof(array[0]) );
	//如果按照默认的方式进行排序那么排序的结果就是升序

	//如果我们需要进行降序排列怎么办呢?
	//这时候就需要用到仿函数了
	//我们需要通过仿函数或者函数指针来改变排序的规则
	for (auto& e : array)
		cout << e << " ";
	cout << endl;
	//用仿函数生成函数对象
	Greater<int> gr;

	std::sort(array, array + sizeof(array) / sizeof(array[0]),gr);

	for (auto& e : array)
		cout << e << " ";

	//用函数指针传参进行排序
	std::sort(array, array + sizeof(array) / sizeof(array[0]), FunGreater);
	//仿函数和函数指针的效果是相同的
}

        但是如果要排序的元素为自定义类型时,需要用户定义排序规则。 

struct Goods
{
	string _name;//名字
	double _price;//价格
	int _num;//数量
	Goods(const char* str, double price,int num)
		:_name(str)
		,_price(price)
		,_num(num)
	{ }
};
void TestSort1()
{
	vector<Goods> v1 = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };
	//现在需要对自定义类型进行排序
	//如果按照_name排序我们需要实现至少两个仿函数
	//按照_price排序也需要两个仿函数
	//按照_num排序也需要两个仿函数
	//这样的话就需要我们实现很多的仿函数,如果仿函数的命名风格不好的话,别人对于这些仿函是干什么的必然不好理解,从这里也可以看出好的命名风格也是很重要的
}

       现在需要对自定义类型进行排序,如果按照_name排序我们需要实现至少两个仿函数,按照_price排序也需要两个仿函数, 按照_num排序也需要两个仿函数, 这样的话就需要我们实现很多的仿函数,如果仿函数的命名风格不好的话,别人对于这些仿函是干什么的必然不好理解,从这里也可以看出好的命名风格也是很重要的。 

struct Goods
{
	string _name;//名字
	double _price;//价格
	int _num;//数量
	Goods(const char* str, double price,int num)
		:_name(str)
		,_price(price)
		,_num(num)
	{ }
};
struct ComparePriceLess
{
	bool operator()(const Goods &g1, const Goods &g2)
	{
		return g1._price < g2._price;
	}
};
struct ComparePriceMore
{
	bool operator()(const Goods& g1, const Goods& g2)
	{
		return g1._price > g2._price;
	}
};
struct CompareNumLess
{
	bool operator()(const Goods& g1, const Goods& g2)
	{
		return g1._num < g2._num;
	}
};
struct CompareNumMore
{
	bool operator()(const Goods& g1, const Goods& g2)
	{
		return g1._num > g2._num;
	}
};
struct CompareNameLess
{
	bool operator()(const Goods& g1, const Goods& g2)
	{
		return g1._name < g2._name;//string重载了operator>和operator<所以可以直接比较

	}
};
struct CompareNameMore
{
	bool operator()(const Goods& g1, const Goods& g2)
	{
		return g1._name > g2._name;
	}
};
void TestSort1()
{
	vector<Goods> v1 = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };
	//现在需要对自定义类型进行排序
	//如果按照_name排序我们需要实现至少两个仿函数
	//按照_price排序也需要两个仿函数
	//按照_num排序也需要两个仿函数
	//这样的话就需要我们实现很多的仿函数,如果仿函数的命名风格不好的话,别人对于这些仿函是干什么的必然不好理解,从这里也可以看出好的命名风格也是很重要的
	std::sort(v1.begin(), v1.end(), ComparePriceMore());
	std::sort(v1.begin(), v1.end(), CompareNumMore());
	std::sort(v1.begin(), v1.end(), CompareNameMore());

}

int main()
{
	TestSort1();
	return 0;
}

        为了提高代码的可读性,所以在这里引入了lambda表达式。 

2.lambda表达式语法

        上述的问题也可以使用lambda表达式来解决。

void TestSort2()
{
	vector<Goods> v1 = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };
	std::sort(v1.begin(), v1.end(), [](const Goods& g1, const Goods& g2) ->bool {return g1._name > g2._name;});
	std::sort(v1.begin(), v1.end(), [](const Goods& g1, const Goods& g2)->bool {return g1._num > g2._num;});
	std::sort(v1.begin(), v1.end(), [](const Goods& g1, const Goods& g2)->bool {return g1._price > g2._price;});
}

        实际上我们可以看到lambda表达式就是匿名函数。 

        lambda表达式的语法: 

        lambda表达式书写格式:[ capture-list ]( parameters ) mutable -> return -type{statement} 

        1.lambda表达式各部分说明 

        [capture]:捕捉列表,该列表总是出现在lambda表达式的开头位置 ,编译器根据[]来判断接下来的代码是否为lambda表达式,捕捉列表能够捕捉上下文中的变量供lambda函数使用

        [paremeters]:参数列表。与普通函数的参数列表一样,如果不需要参数传递,则可以连同()一起省略。

        mutable:默认情况下,lambda函数的总是一个const函数,mutable可以取消其常属性。使用该修饰符时,参数列表不可以省略。(即使参数为空)

        ->returntype:返回值类型。用来追踪返回类型形式声明函数的返回值类型,若果没有返回值时该部分可以省略,返回值类型明确的情况下也可以省略,由编译器对返回值类型进行推导。

        {statement}:函数体。该函数体内除了可以使用其参数外,还可以使用所捕获到的变量。

注意:

在lambda函数定义中,参数列表和返回值类型是可选部分,而捕捉列表和函数体可以为空。因此C++11最简单的lambda表达式为[]{};该lambda表达式不做任何事情

        

int main()
{
	//最简单的lambda表达式
	[] {};//但是这个表达式没有意义
	//省略了参数列表和返回值类型返回值类型由编译器推导为int
	int a = 3,b = 4;
	[=] {return a + b;};

	//省略了返回值类型,无返回值类型
	auto fun1 = [&] (int c){b = a + c;};//由auto自动推导的一个对象
	fun1(10);
	cout << b << endl;
	int c = 0;
	//各部分都很完善的lambda表达式
	auto fun2 = [=, &b]()->int {return b += a + c;};

	//复制捕捉x
	int x = 10;
	auto add_x = [x](int a)mutable ->int {x *= 2;return a + x;};

	return 0;
}

         通过上述的例子可以看出实际上lambda表达式可以理解为无名函数,该函数无法直接调用,需要通过auto将其赋值给一个变量。

        2.捕捉列表说明

        捕捉列表描述了上下文中哪些数据可以被lambda表达式使用,以及使用的方式是传值还是传引用。

        [val]:表示值传递捕获变量val。

        [=]:表示值传递捕获所有父作用域中的变量(包括this)。

        [&val]:表示引用捕获变量val。

        [&]:表示引用捕获所有的变量(包括this)。

        [this]:表示值传递的方式捕获this指针。

        注意:

        a.父作用域指包含lambda表达式的语句块 

        b.语法上捕捉列表由多个捕捉项组成,并以逗号分隔

        比如:[=,&b,&b];以引用方式捕捉变量a和b,使用值捕捉的方式捕捉其它变量。

                   [&,a,this]:以值捕捉的方式捕捉a和this指针,以引用的捕捉方式捕捉其它的值。 

        c.捕捉列表不允许变量重复传递,否则就会导致编译错误

        比如:[=,a];以值的方式捕捉所有变量,然后又捕捉a变量重复了。

        d. 在块作用域以外的lambda函数的捕捉列表必须为空

        e.在块作用域中的lambda表达式仅能捕捉父作用域中的局部变量,捕捉任何非此局部域的或者非局部变量都会导致编译报错

        f.lambda表达式之间不能互相赋值,即使看起来类型相同

void (*PF)();
int main()
{
	auto f1 = [] {cout << "hello world" << endl;};
	auto f2 = [] {cout << "hello world" << endl;};
	//f1 = f2;//此处会编译失败,因为lambda表达式的底层编译器会将它替换成一个仿函数;

	auto f3(f2);//允许构造拷贝一个新的副本
	f3();
	//可以将lambda表达式赋值给相同类型的指针
	PF = f2;
	PF();
	return 0;
}

 

3.函数对象和lambda表达式

        函数对象又叫做仿函数,可以像函数一样使用的对象,就是在类中重载了operator()运算符的类对象。

class Rate
{
public:
	Rate(double rate)
		:_rate(rate)
	{}
	double operator()(double year, double money)
	{
		return money * year * _rate;
	}
private:
	double _rate;
};
int main()
{
	//函数对象
	double rate = 0.90;
	Rate r1(rate);
	r1(10000, 2);

	//lambda
	auto r2 = [=](double money, double year)->double {return money * year * rate;};
	r2(10000, 2);
	return 0;
}

        从使用方来看lambda表达式和函数对象完全一样,函数对象将rate作为其成员变量,定义时给初始值就好了,lambda表达式通过捕捉列表来对rate进行捕捉。

 

        实际上在底层编译器对lambda表达式的处理是完全按照函数对象进行处理的,即如果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载operator()。 调用lambda表达式时,编译器会给它起一个名字lambda_uuid,(唯一的名字,然后去调用它)。

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

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

相关文章

STM32使用FAT文件系统-代码简读

FAT的一些基础知识、概念先看我这一篇&#xff1a; FAT32文件和目录的组织方式_fat32文件系统架构设计_暴躁的野生猿的博客-CSDN博客 fat文件系统的所有外部接口都在ff.h中 物理驱动器、逻辑驱动器 一个物理驱动器就是一个真实的存储设备&#xff0c;例如一个硬盘、一个内存…

抓拍摄像机开关量控制4K高清手机远程看图建筑生长定时缩时相机

作为物联网数据采集解决方案专业提供商,数采物联网小编daq-iot 在这里做以下内容介绍,并诚挚的欢迎大家讨论和交流。 项目案例参考视频&#xff1a; https://www.bilibili.com/video/BV1Kp4y1T7wQ/?spm_id_from333.999.0.0 4K高清太阳能供电定时拍照相机&#xff0c;通过光…

无涯教程-JavaScript - HEX2BIN函数

描述 HEX2BIN函数将十六进制数转换为二进制数。 语法 HEX2BIN (number, [places])争论 Argument描述Required/Optionalnumber 您要转换的十六进制数。 数字不能超过10个字符(40位)。数字的最高有效位是符号位(从右数第40位)。其余的39位是幅度位。 负数使用二进制补码表示。…

人工智能在电子商务中的突破性优势

最近都听说人工智能&#xff08;AI&#xff09;吗&#xff1f;电子商务的人工智能方面尤其受欢迎。当您以正确的方式使用正确的 AI技术时&#xff0c;您可以彻底改变您的经营方式。AI可帮助您节省时间、减少手动工作并提高数据的质量和准确性。 从本质上讲&#xff0c;您现在可…

移动硬盘或U盘无法弹出的解决方法

以下内容源于网络资源的学习与整理&#xff0c;如有侵权请告知删除。 最近在红米本win11中总遇到“该设备正在使用中”而无法弹出硬盘的问题。 解法该问题的思路&#xff1a;先定位占用该设备的进程&#xff0c;然后结束该进程。 定位进程 既然设备被占用&#xff0c;那肯定…

分享一下有哪些微信营销活动

微信营销活动是商家利用微信平台进行宣传和推广的一种重要方式。通过精心策划的微信营销活动&#xff0c;商家可以吸引更多的潜在客户&#xff0c;提高品牌知名度和销售额。本文将介绍几种常见的微信营销活动。 一、抽奖活动 抽奖活动是微信营销中一种非常常见的活动形式。商家…

MySQL:区分大小写

查看MySQL版本 show variables; 1、查看 MySQL 当前的区分大小写设置&#xff1a; SHOW VARIABLES LIKE lower_case_table_names; 或者 show Variables like %table_names 2、更改大小写敏感设置&#xff1a; 在 MySQL 5.7 中&#xff0c;更改大小写敏感设置要求修改配置文件 …

2023/9/7 -- C++/QT

作业 1> 思维导图 2> 封装一个结构体&#xff0c;结构体中包含一个私有数组&#xff0c;用来存放学生的成绩&#xff0c;包含一个私有变量&#xff0c;用来记录学生个数&#xff0c; 提供一个公有成员函数&#xff0c;void setNum(int num)用于设置学生个数 提供一个…

赠书福利开始啦—〖Effective软件测试〗

文章目录 ❤️赠书——《Effective软件测试》&#x1f31b;书籍简介&#x1f31b;图书链接❤️活动介绍 ❤️赠书——《Effective软件测试》 &#x1f31b;书籍简介 &#x1f31f;专家赞誉 本书是一本内容深刻的软件测试书籍&#xff0c;讲述如何平衡研发效率和整体质量来更高…

使用 UPFC 计算电力系统网络潮流(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Ubuntu yolov5 环境配置

查看Ubuntu版本 $ cat /proc/version Linux version 5.4.0-150-generic (builddbos03-amd64-012) (gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)) #167~18.04.1-Ubuntu SMP Wed May 24 00:51:42 UTC 2023虚拟机磁盘扩容 因为在环境搭建过程中遇到了磁盘空间不足的问题&a…

索尼 toio™ 应用创意开发征文|探索创新的玩乐世界——索尼 toio™

导语&#xff1a; 在技术的不断进步和发展中&#xff0c;玩具也逐渐融入了智能化的潮流。索尼 toio™作为一款前沿的智能玩具&#xff0c;给孩子和成人带来了全新的游戏体验。本文将介绍索尼 toio™的特点、功能和应用场景&#xff0c;让读者了解这个令人兴奋的创新产品。 1. 了…

抢先一步,新华三携手HPE正在重塑AI存储新格局

当前智能IT的创新 正呈现出蓬勃发展之势 【全球存储观察 &#xff5c; 热点关注】 当前&#xff0c;数字经济在中国获得了长足进步&#xff0c;并驱动着算力快速进化。为此&#xff0c;智能IT的创新也呈现出了前所未有的蓬勃之势。 从智能存储中枢发布到现在&#xff0c;新华…

LAMP搭建wordpress并使用reids加速网页

L linux A apache hhtpd M mysql/maridb P PHP1、 安装php rpm -ivh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm yum install -y --enablereporemi --enablereporemi-php72 php php-opcache php-devel php-mysqlnd php-gd php-redis2、 安装mysql5.7 2.1、…

考完试家长怎么查看孩子成绩和等级?

考试结束了&#xff0c;孩子们的成绩和等级也出来了&#xff0c;对于家长来说&#xff0c;如何快速方便地查看孩子的成绩和等级呢&#xff1f;今天&#xff0c;我要向大家介绍一个非常实用的工具——易查分&#xff0c;让家长们便捷高效了解孩子的学习成果。 好消息&#xff01…

如何封装自动化测试框架?(超详细~)

封装自动化测试框架&#xff0c;测试人员不用关注框架的底层实现&#xff0c;根据指定的规则进行测试用例的创建、执行即可&#xff0c;这样就降低了自动化测试门槛&#xff0c;能解放出更多的人力去做更深入的测试工作。 本篇文章就来介绍下&#xff0c;如何封装自动化测试框…

【已解决】ognl.PropertyAccessor

在Spring boot2.x用TemplateEngine处理数据得时候&#xff0c;出现以下错误&#xff1a; 定位到代码行&#xff1a; 解决办法&#xff1a;修改thymeleaf的依赖&#xff1a; <!-- thymeleaf --><dependency><groupId>org.thymeleaf</groupId><…

低功耗窗帘电机解决方案成功应用并通过 Matter 1.1 认证

Nordic Semiconductor官方宣布与HooRii Tech&#xff08;和众科技&#xff09;携手合作&#xff0c;基于 Nordic nRF52840 芯片平台打造的 HRN71模组&#xff0c;成功赋能低功耗窗帘电机品牌发布Matter产品。低功耗窗帘电机获得 Matter 1.1 认证意味着它具有与其他 Matter 认证…

C. Ntarsis‘ Set

Problem - C - Codeforces 思路&#xff1a;这个题求一个满足条件的最小的&#xff0c;我们可以想到二分可以求满足条件的最小值&#xff0c;我们考虑二分答案&#xff0c;当当前的枚举的为mid时&#xff0c;我们考虑它会怎样变化&#xff0c;首先一开始mid的排名就是mid&#…

Python,Bytetrack 源码解读,参数,源码解释,逐句分析代码,目标追踪

文章目录 1、得到索引2、高得分框参与匹配&#xff0c;可能会留下有匹配不了的框3、低得分框参与匹配4、处理 unconfirmed 匹配5、创建新的【STrack对象】6、扔掉太久没匹配到框的【STrack对象】7、输出追踪框 1、得到索引 self.args.track_thresh是轨迹阈值。轨迹的得分是iou…