C++中的lambda表达式

news2024/11/13 9:15:41

在这里插入图片描述

引入:
首先来看一个例子

struct fruit
{
	double _price;
	int _evalute;
	string _name;
	fruit(const char* str, int a, double price)
		:_name(str)
		,_evalute(a)
		,_price(price)
	{

	}
};
struct ComparePriceGreater
{
	bool operator()(const fruit& g1, const fruit& gr)
	{
		return g1._price > gr._price;
	}
};
struct ComparePriceLess
{
	bool operator()(const fruit& g1, const fruit& gr)
	{
		return g1._price < gr._price;
	}
};

int main()
{
	vector<fruit> v = { {"苹果",3,2.5},{"香蕉",2,3.5},{"梨子",5,5.5} };
	sort(v.begin(), v.end(), ComparePriceGreater());//不知道怎么去比较,所以我们要传入一个仿函数
	sort(v.begin(), v.end(), ComparePriceLess());
	return 0;
}

在这里插入图片描述

 如图,我们知道,std::sort函数在排序时,如果是内置类型,就默认是升序排列,但是如果是自定义类型呢?在lambda表达式引入之前,我们通常是写一个仿函数,然后重载(),从而达到排序的效果。
 但是,每次为了实现这样一个算法,就都要去重新写出一个类。特别是相同类的命名问题,都给我们带来了很大的不方便。
 lambda表达式是在C++11引入的语法,一般用于定义匿名函数,使代码更加灵活方便。
lambda表达式的格式

[capture-list] (parameters) mutable -> return-type { statement}

各个部分的说明

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

  • parameters:参数列表,与普通的函数很是相似,如果不需要传递参数,直接省略即可,甚至可以连()一起省略。

  • mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表括号不能省略。

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

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

 如上边所说,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为空。因此C++11中最简单的lambda函数为:[]{}; 该lambda函数不能做任何事情。
捕捉列表说明
 捕捉列表描述了上下文中那些数据可以被lambda使用,以及使用的方式传值还是传引用。
 [var]:表示值传递方式捕捉变量var
在这里插入图片描述

 [=]:表示值传递方式捕获所有父作用域中的变量(包括this)
在这里插入图片描述

 [&var]:表示引用传递捕捉变量var
在这里插入图片描述

 [&]:表示引用传递捕捉所有父作用域中的变量(包括this)。
在这里插入图片描述

 [this]:表示值传递方式捕捉当前的this指针,上述例子中=改为this,大家可以尝试一下。
注意事项
a. 父作用域指包含lambda函数的语句块,即包含该lambda表达式的大括号中。

b. 语法上捕捉列表可由多个捕捉项组成,并以逗号分割。
在这里插入图片描述

 当然可以不同类型的捕捉。比如:[=, &a, &b]:以引用传递的方式捕捉变量a和b,值传递方式捕捉其他所有变量。[&,a, this]:值传递方式捕捉变量a和this,引用方式捕捉其他变量。
c. 捕捉列表不允许变量重复传递,否则就会导致编译错误。比如:[=, a]:=已经以值传递方式捕捉了所有变量,捕捉a重复。
在这里插入图片描述

d. 在块作用域以外的lambda函数捕捉列表必须为空。
e. 在块作用域中的lambda函数仅能捕捉父作用域中局部变量,捕捉任何非此作用域或者非局部变量都会导致编译报错。
f. lambda表达式之间不能相互赋值,即使看起来类型相同。
使用lambda表达式改进上边的代码

struct fruit
{
	double _price;
	int _evalute;
	string _name;
	fruit(const char* str, int a, double price)
		:_name(str)
		,_evalute(a)
		,_price(price)
	{

	}
};
int main()
{
	vector<fruit> v = { {"苹果",3,2.5},{"香蕉",2,3.5},{"梨子",5,5.5} };
	sort(v.begin(), v.end(), [](const fruit& f1, const fruit& f2) {return f1._price > f2._price; });
	sort(v.begin(), v.end(), [](const fruit& f1, const fruit& f2) {return f1._price < f2._price; });
	return 0;
}

通过上述代码,我们可以很清晰的看出代码更加简洁,没有那么冗余了。
仿函数和lambda表达式
 仿函数就是重载()运算符的对象,lambda表达式和仿函数功能很是相似,都可以作为仿函数表达式进行传递,那么他们的底层都是如何运转的呢?
我们来细细探究探究
 其实我们可以将lambda当做一个匿名函数对象
在这里插入图片描述
转到反汇编来观察是如何实现的。
在这里插入图片描述
 可以看出,lambda实际上会作为一个匿名函数对象进行传递,函数名省略,函数参数和函数体和仿函数相同。就像范围for一样,看着感觉会很复杂,其实底层还是使用迭代器进行遍历,一切为了方便而已。
我们还可以利用typeid来查看函数类型。
在这里插入图片描述

编译器是如何看lambda呢?其实就像仿函数一样。

class LambdaClass
{
public:
    int operator () (int a, int b) const
    {
        return a + b;
    }
};

LambdaClass plus;
int c = plus(1, 2);

 lambda表达式会把主要的部分提取出来,让我们的代码编写更加简便。
 那么捕获列表中捕获的变量呢?其实就是作为类的成员变量,如果是值拷贝,那么我们重载的()函数就是const函数的,是无法修改捕获的值的。

class LambdaClass
{
public:
    LambdaClass(int xx, int yy)
    : x(xx), y(yy) {}

    int operator () (int a, int b) const
    {
        return x + y + a + b;
    }

private:
    int x;
    int y;
}

int x = 1; int y = 2;
LambdaClass plus(x, y);
int c = plus(1, 2);

 如果我们想要修改捕获的值呢?如改变上边的x,只需要加上关键字mutable。

int x = 1; int y = 2;
auto plus = [=] (int a, int b) mutable -> int { x++; return x + y + a + b; };
int c = plus(1, 2);

如果是值引用呢?
我们只需要进行引用捕获即可。

class LambdaClass
{
public:
    LambdaClass(int& xx, int& yy)
    : x(xx), y(yy) {}

    int operator () (int a, int b)
    {
        x++;
        return x + y + a + b;
    }

private:
    int &x;
    int &y;
};

 此时在函数中改变传入的值就可以改变捕获的值。
 对应仿函数,我们可以最后再把lambda表达式和仿函数类的各个成分对引起来就是如下关系。

捕获列表,对应LambdaClass类的private成员。
参数列表,对应LambdaClass类的成员函数的operator()的形参列表
mutable,对应 LambdaClass类成员函数 operator() 的const属性 ,但是只有在捕获列表捕获的参数不含有引用捕获的情况下才会生效,因为捕获列表只要包含引用捕获,那operator()函数就一定是非const函数。
返回类型,对应 LambdaClass类成员函数 operator() 的返回类型
函数体,对应 LambdaClass类成员函数 operator() 的函数体。

注意:引用捕获和值捕获不同的一点就是,对应的成员是否为引用类型。
本文结束,如果有问题还请及时提出,我会虚心改正,感谢大家的观看。

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

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

相关文章

redis在docker安装并启动流程

1、启动server docker run -d -p 6379:6379 --name redis01 redis:7.2.4以上命令&#xff0c;每次启动新的Redis容器&#xff0c;数据会丢失。 我们需要挂载数据文件&#xff0c;在宿主机上面&#xff0c;这样就可以持久化数据. 2、挂载数据文件&#xff08;可根据需求选择…

Linux常用操作命令(清单快查版)

Linux常用操作命令&#xff0c;今日先给出快查清单&#xff0c;后续出带命令参数及不同OS的区别语法的相关示例 1. 文件与目录操作 命令描述ls列出目录内容cd切换目录pwd显示当前工作目录mkdir创建目录rmdir删除空目录cp复制文件或目录mv移动或重命名文件或目录rm删除文件或目…

(异步编程)前端八股文修炼Day3

一异步编程异步编程的实现方式&#xff1f; 在 JavaScript 中&#xff0c;异步编程是处理异步操作的重要部分&#xff0c;常见的异步编程实现方式有以下几种&#xff1a; 回调函数&#xff08;Callbacks&#xff09;&#xff1a;回调函数是最基本的异步编程方式&#xff0c;通…

【第三方登录】Google邮箱

登录谷歌邮箱开发者 https://console.developers.google.com/ 先创建项目 我们用的web应用 设置回调 核心主要&#xff1a; 1.创建应用 2.创建客户端ID 3.设置域名和重定向URL 4.对外公开&#xff0c;这样所有的gmail邮箱 都能参与测试PHP代码实现 引入第三方包 h…

智慧园区楼宇AI解决方案

背景 人工智能对于人类的影响要比工业革命发生的速度快10倍,规模大 300倍,影响几乎大3000倍 - 麦肯锡全球研究院;2017年7月20日,国务院印发《新一代人工智能发展规划》,首次把人工智能发展上升为国家战略层面,全面布局面向2030年的中国人工智能发展整体规划;中美同时进…

解密Google Cloud 全新 PaLM2及创新应用

&#x1f4f8;背景 因长期在大模型相关的部门工作&#xff0c;每天接收到很多和AI相关的信息&#xff0c;但小编意识到目前理解到的一些AI知识还有些片面。 恰逢稀土掘金开发者大会有谈到大模型相关的知识&#xff0c;于是借此机会&#xff0c;对大模型相关的一些知识再了解一…

GuLi商城-商品服务-API-三级分类-查询-树形展示三级分类数据

1、网关服务配置路由 2、商品服务 3、启动本地nacos&#xff0c;打开nacos地址看nacos服务列表 4、编写VUE <template> <el-tree :data"menus" :props"defaultProps" node-click"handleNodeClick"></el-tree> </template…

【自我提升】计算机领域相关证书

目录 计算机技术与软件专业资格&#xff08;水平&#xff09;考试证书&#xff08;软考&#xff09;Oracle认证Cisco认证微软认证红帽认证AWS认证 计算机技术与软件专业资格&#xff08;水平&#xff09;考试证书&#xff08;软考&#xff09; 计算机技术与软件专业技术资格&a…

做自配送平台,商家如何发单?平台可以接收那些订单?

为了增加品牌曝光&#xff0c;许多商家选择加入外卖平台&#xff0c;然而随着时间推移&#xff0c;一些问题也逐渐显现&#xff1a;大平台对商家的配送抽佣越来越高&#xff0c;很多商家都选择自配送来降本增效。 但是问题来了&#xff01;目前市面上没有一款产品是自动发单到…

栈应用之---括号匹配

题意描述&#xff1a; 在算术表达式中&#xff0c;除了加、减、乘、除等运算外&#xff0c;往往还有括号。包括有大括号{}&#xff0c;中括号[]&#xff0c;小括号()&#xff0c;尖括号<>等。 对于每一对括号&#xff0c;必须先左边括号&#xff0c;然后右边括号&#xf…

【Excel表格中如何将单元格数据复制粘贴到合并后的单元格中】

要实现的效果如下&#xff1a; 方法一、使用插件 1、下载“方方格子”插件 下载地址&#xff1a;http://www.ffcell.com/home/ffcell.aspx 2、下载完成后&#xff0c;启动WPS或Excel软件&#xff0c;同意添加插件&#xff0c;选择【方方格子】-【复制粘贴】-【复制到合并区域…

现代化应用部署工具-Docker

Docker 简介 什么是Docker Docker 是一个开源的应用容器引擎&#xff0c;可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上。 Docker部署的优势 通过使用Docker等容器技术&#xff0c;可以将应用程序及其依赖项…

安卓转鸿蒙竟如此丝滑

随着鸿蒙的爆火&#xff0c;大家都想知道鸿蒙能不能搞&#xff1f; 相信大家搞开发的&#xff0c;都多多少少的了解过鸿蒙。近几个月鸿蒙的大动作也不少&#xff0c;如&#xff1a;重庆市近20个垂域应用与鸿蒙原生合作、深圳制定鸿蒙《行动计划》、阿里再次与鸿蒙展开合作&…

学习次模函数-第1章 引言

许多组合优化问题可以被转换为集合函数的最小化&#xff0c;集合函数是在给定基集合的子集的集合上定义的函数。同样地&#xff0c;它们可以被定义为超立方体的顶点上的函数&#xff0c;即&#xff0c;其中是基集合的基数-它们通常被称为伪布尔函数[27]。在这些集合函数中&…

“大变局开启”!比特币出现“资金出逃”!以太币ETF获批“乌云重重”!

比特币(BTC)在周(24)日最低来到63772美元之后&#xff0c;就持续震荡上涨。今(25)晨七点左右最高更逼近67628美元。以太坊(ETH)走势与BTC接近&#xff0c;清晨最高触及3471美元之后有所回调。 对于比特币的疲惫走势&#xff0c;业内人士指出&#xff0c;近期比特币价格创下新高…

OSCP靶场--Cockpit--待续

OSCP靶场–Cockpit 考点(sql注入绕过sudo tar提权) 1.nmap扫描 ## ┌──(root㉿kali)-[~/Desktop] └─# nmap 192.168.229.10 -Pn -sV -sC --min-rate 2500 Starting Nmap 7.92 ( https://nmap.org ) at 2024-03-25 01:40 EDT Nmap scan report for 192.168.…

向上生长

&#xff08;1&#xff09; 我记得2010年&#xff0c;在中国的苹果应用商店里&#xff0c;充斥的App还有很多&#xff1a;日历App、天气App、电池省电App、记事本App…。但这已经过去了2007-2008-2009三年&#xff0c;这些应用仍然很欢。 我有一个朋友算是中国最早一批开发iOS …

promethus的安装使用

1、# 软件下载地址 https://prometheus.io/download/ https://grafana.com/grafana/download https://prometheus.io/download/ Prometheus是一套开源的监控&报警&时间序列数据库的组合,起始是由SoundCloud公司开发的。 Prometheus 的优点 1、非常少的外部依赖,安装…

普通员工如何快速成为领导 管理与领导的区别 你有想过你缺哪?

一切为了生存。 我的总结&#xff08;我居然从来没想过&#xff09; 1、领袖&#xff1a;领人导事。规划能力&#xff0c;画饼&#xff0c;对结果赏罚。用人&#xff0c;什么人能放心干成。 2、管理&#xff1a;管人理事。对人清楚&#xff0c;对事清楚。 3、通用能力&#x…

今日大模型论文 || 清华微软联合打造LLMLingua2,速度提升达2.9倍

引言&#xff1a;探索任务无关的提示压缩技术 在大型语言模型&#xff08;LLMs&#xff09;的应用中&#xff0c;提示&#xff08;prompts&#xff09;的使用已成为一种常见的技术&#xff0c;它通过丰富而信息量大的提示来处理复杂和多样化的任务。然而&#xff0c;这些提示往…