C++_Lambda表达式的完整介绍

目录

1. 什么是Lambda表达式

1.1 四种表达式的含义

1.2 lambda表达式各个成员的解释

2. 捕获列表

3. 编译器如何看待Lambda表达式

参考文章


 

参考: C++ Lambda表达式的完整介绍 - 知乎

c++在c++11标准中引入了lambda表达式,一般用于定义匿名函数,使得代码更加灵活简洁。lambda表达式与普通函数类似,也有参数列表、返回值类型和函数体,只是它的定义方式更简洁,并且可以在函数内部定义。

1. 什么是Lambda表达式

最常见的lambda的表达式写法如下

auto plus = [] (int v1, int v2) -> int { return v1 + v2; }
int sum = plus(1, 2);

这里只是计算两个数的和,我们一般情况下肯定是不会这么用的,更多的时候,我们都是和stl的一些算法结合使用,例如自定义一个结构体的排序规则和打印。

struct Item
{Item(int aa, int bb) : a(aa), b(bb) {} int a;int b;
};int main()
{std::vector<Item> vec;vec.push_back(Item(1, 19));vec.push_back(Item(10, 3));vec.push_back(Item(3, 7));vec.push_back(Item(8, 12));vec.push_back(Item(2, 1));// 根据Item中成员a升序排序std::sort(vec.begin(), vec.end(),[] (const Item& v1, const Item& v2) { return v1.a < v2.a; });// 打印vec中的item成员std::for_each(vec.begin(), vec.end(),[] (const Item& item) { std::cout << item.a << " " << item.b << std::endl; });return 0;
}

这样的写法让我们代码更加简洁、清晰,可读性更强。

在c++的官方文档中,给出了lamda表达式的四种写法,这里知乎的排版有点难用,所以直接在官方文档上截了一个图。

下面介绍一下lambda的四种表达式的含义,以及表达式中各个成分的,其实说白就是在自己理解的基础上翻译一下官方文档。

1.1 四种表达式的含义

(1)完整的lambda表达式,包含了lambda表达式的所有成分。

(2)常量lambda表达式,捕获的变量都是常量,不能在lambda表达式的body中进行修改。

(3)和(2)基本一致,唯一的区别就是,lambda表达式的函数返回值可以通过函数体推导出来。一般情况函数返回值类型明确或者没有返回值的情况下可以这样写。

(4)lambda表达式的函数没有任何参数,但是可以添加lambda-specifiers,lambda-specifiers是什么我们后续再介绍。

1.2 lambda表达式各个成员的解释

captures 捕获列表,lambda可以把上下文变量以值或引用的方式捕获,在body中直接使用。

tparams 模板参数列表(c++20引入),让lambda可以像模板函数一样被调用。

params 参数列表,有一点需要注意,在c++14之后允许使用auto左右参数类型。

lambda-specifiers lambda说明符, 一些可选的参数,这里不多介绍了,有兴趣的读者可以去官方文档上看。这里比较常用的参数就是mutable和exception。其中,表达式(1)中没有trailing-return-type,是因为包含在这一项里面的。

trailing-return-type 返回值类型,一般可以省略掉,由编译器来推导。

body 函数体,函数的具体逻辑。

2. 捕获列表

上面介绍完了lambda表达式的各个成分,其实很多部分和正常的函数没什么区别,其中最大的一个不同点就是捕获列表。我在刚开始用lambda表达式的时候,还一直以为这个没啥用,只是用一个 [] 来标志着这是一个lambda表达式。后来了解了才知道,原来这个捕获列表如此强大,甚至我觉得捕获列表就是lambda表达式的灵魂。下面先介绍几种常用的捕获方式。

[] 什么也不捕获,无法lambda函数体使用任何

[=] 按值的方式捕获所有变量

[&] 按引用的方式捕获所有变量

[=, &a] 除了变量a之外,按值的方式捕获所有局部变量,变量a使用引用的方式来捕获。这里可以按引用捕获多个,例如 [=, &a, &b,&c]。这里注意,如果前面加了=,后面加的具体的参数必须以引用的方式来捕获,否则会报错。

[&, a] 除了变量a之外,按引用的方式捕获所有局部变量,变量a使用值的方式来捕获。这里后面的参数也可以多个,例如 [&, a, b, c]。这里注意,如果前面加了&,后面加的具体的参数必须以值的方式来捕获。

[a, &b] 以值的方式捕获a,引用的方式捕获b,也可以捕获多个。

[this] 在成员函数中,也可以直接捕获this指针,其实在成员函数中,[=]和[&]也会捕获this指针。

#include <iostream>int main()
{int a = 3;int b = 5;// 按值来捕获auto func1 = [a] { std::cout << a << std::endl; };func1();// 按值来捕获auto func2 = [=] { std::cout << a << " " << b << std::endl; };func2();// 按引用来捕获auto func3 = [&a] { std::cout << a << std::endl; };func3();// 按引用来捕获auto func4 = [&] { std::cout << a << " " << b << std::endl; };func4();
}

3. 编译器如何看待Lambda表达式

我们把lambda表达式看成一个函数,那编译器怎么看待我们协的lambda呢?

其实,编译器会把我们写的lambda表达式翻译成一个类,并重载 operator()来实现。比如我们写一个lambda表达式为

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

那么编译器会把我们写的表达式翻译为

// 类名是我随便起的
class LambdaClass
{
public:int operator () (int a, int b) const{return a + b;}
};LambdaClass plus;
int c = plus(1, 2);

调用的时候编译器会生成一个Lambda的对象,并调用opeartor ()函数。(备注:这里的编译的翻译结果并不和真正的结果完全一致,只是把最主要的部分体现出来,其他的像类到函数指针的转换函数均省略

上面是一种调用方式,那么如果我们写一个复杂一点的lambda表达式,表达式中的成分会如何与类的成分对应呢?我们再看一个 值捕获 例子。

int x = 1; int y = 2;
auto plus = [=] (int a, int b) -> int { 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) 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);

其实这里就可以看出,值捕获时,编译器会把捕获到的值作为类的成员变量,并且变量是以值的方式传递的。需要注意的时,如果所有的参数都是值捕获的方式,那么生成的operator()函数是const函数的,是无法修改捕获的值的,哪怕这个修改不会改变lambda表达式外部的变量,如果想要在函数内修改捕获的值,需要加上关键字 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);

我们再来看一个引用捕获的例子。

int x = 1; int y = 2;
auto plus = [&] (int a, int b) -> 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;
};

我们可以看到以引用的方式捕获变量,和值捕获的方式有3个不同的地方:1. 参数引用的方式进行传递; 2. 引用捕获在函数体修改变量,会直接修改lambda表达式外部的变量;3. opeartor()函数不是const的。

针对上面的集中情况,我们把lambda的各个成分和类的各个成分对应起来就是如下的关系:

捕获列表,对应LambdaClass类的private成员

参数列表,对应LambdaClass类的成员函数的operator()的形参列表

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

返回类型,对应 LambdaClass类成员函数 operator() 的返回类型

函数体,对应 LambdaClass类成员函数 operator() 的函数体。

引用捕获和值捕获不同的一点就是,对应的成员是否为引用类型。

参考文章

Lambda expressions

C++ Lambda 编译器实现原理

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

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

相关文章

云HIS为连锁医院机构提供统一医院管理解决方案

云HIS重建统一的信息架构体系&#xff0c;重构管理服务流程&#xff0c;重造病人服务环境&#xff0c;向不同类型的医疗机构提供SaaS化HIS服务解决方案。 云HIS优势 1、云端数据优势 在传统的HIS模式里&#xff0c;数据存于医院本身的服务器机组&#xff0c;一旦发生故障&…

【QA】Linux-CentOS彻底卸载MySQL

文章概述 Linux系统下&#xff0c;若要卸载干净默认的MySQL&#xff0c;从而安装新的版本&#xff0c;需要卸载干净&#xff0c;不然会在不经意间影响后续安装 文章演示的系统&#xff1a;Linux CentOS 卸载步骤 1、查询是否安装了MySQL [rootlocalhost ~]# rpm -qa | gre…

unity打包apk后网络请求提示unknown error处理

近期同事的一个比较老的版本的unity项目在电脑上运行都正常&#xff0c;但是打包成android后安装到手机上就提示unknown error 让我帮他排查一下问题。由于是涉密项目不能发图就简单介绍下处理过程吧&#xff01; 一、故障原因 请求的地址ssl证书过期了。 二、处理过程 更改请…

Redis--Geo指令的语法和使用场景举例(附近的人功能)

文章目录 前言Geo介绍Geo指令使用使用场景&#xff1a;附近的人参考文献 前言 Redis除了常见的五种数据类型之外&#xff0c;其实还有一些少见的数据结构&#xff0c;如Geo&#xff0c;HyperLogLog等。虽然它们少见&#xff0c;但是作用却不容小觑。本文将介绍Geo指令的语法和…

竞赛保研 大数据疫情分析及可视化系统

文章目录 0 前言2 开发简介3 数据集4 实现技术4.1 系统架构4.2 开发环境4.3 疫情地图4.3.1 填充图(Choropleth maps)4.3.2 气泡图 4.4 全国疫情实时追踪4.6 其他页面 5 关键代码最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 大数据疫…

Nestjs 图片下载

一、download直接下载 1、添加下载代码 uploadController.ts import { Controller, Get, Post, Body, Patch, Param, Delete, UseInterceptors, UploadedFile, Res } from nestjs/common; import { UploadService } from ./upload.service; import { CreateUploadDto } from…

【面试突击】硬件级别可见性问题面试实战(上)

&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308; 欢迎关注公众号&#xff08;通过文章导读关注&#xff1a;【11来了】&#xff09;&#xff0c;及时收到 AI 前沿项目工具及新技术的推送&#xff01; 在我后台回复…

(2023版)斯坦福CS231n学习笔记:DL与CV教程 (56) | 卷积神经网络

前言 &#x1f4da; 笔记专栏&#xff1a;斯坦福CS231N&#xff1a;面向视觉识别的卷积神经网络&#xff08;23&#xff09;&#x1f517; 课程链接&#xff1a;https://www.bilibili.com/video/BV1xV411R7i5&#x1f4bb; CS231n: 深度学习计算机视觉&#xff08;2017&#xf…

Django ORM 模糊查询实例解析

在 Django 中&#xff0c;对象关系映射 (ORM) 系统提供了一种强大而直观的数据库交互方式。本博文将深入探讨 Django ORM 中的高级单表查询方法和模糊查询选项&#xff0c;使开发人员能够高效地检索和操作数据。 模糊查询是一种非常有用的功能&#xff0c;允许我们根据特定条件…

谷达冠楠科技:抖音开网店新手小白可以卖的产品

随着互联网的发展&#xff0c;越来越多的人选择在网上开设自己的店铺。而抖音作为目前最火的短视频平台&#xff0c;也提供了开店的功能。那么&#xff0c;对于新手小白来说&#xff0c;抖音开网店可以卖哪些产品呢? 我们可以考虑的是服装类商品。抖音上有很多时尚博主&#x…

java httpclient Post

一、Maven引用httpclient <dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.3</version> </dependency> 二、直接上代码 private void PostData(){CloseableHt…

基于Qt/C++的体质监测系统,体重秤,体脂秤,身体质量指数系统,Qt语音播报

目录导览 一、前言二、项目功能介绍三、源程序四、总结 一、前言 可以通过该项目练习的c、qt的知识&#xff0c;提高自己的项目实战经验。同时需要其他管理系统的&#xff0c;如超市购物系统&#xff0c;图书管理系统、实验设备管理系统、超市管理系统、学生管理系统、教师管理…

开发企业微信中的内嵌h5时如何开发与调试

前言&#xff1a; 在我们的项目中&#xff0c;开发企业微信内部的项目的话&#xff0c;分为两种&#xff0c;1种是直接开发企业微信的小程序&#xff0c;另一种则是企业微信内嵌我们的H5界面&#xff0c;我们这里讲一讲企业微信内嵌h5的方法与注意点。 1、开发h5项目 这点没有…

【洛谷 P2084】进制转换 题解(模拟+字符串)

进制转换 题目背景 无 题目描述 今天小明学会了进制转换&#xff0c;比如&#xff08;10101&#xff09;2 &#xff0c;那么它的十进制表示的式子就是 : 1*240*231*220*211*2^0&#xff0c; 那么请你编程实现&#xff0c;将一个M进制的数N转换成十进制表示的式子。 注意…

BGP最通俗易懂的讲解——路由反射器防环机制

路由反射器防环机制&#xff1a;Originator ID与Cluster List 一、Originator ID 该属性属于BGP的可选非过渡属性&#xff1b;Originator ID由RR产生&#xff0c;使用的Router ID的值标识路由的发送者&#xff0c;用于防止集群内产生路由环路&#xff1b;当一条路由第一次被RR…

《每天十分钟》-红宝书第4版-集合引用类型(三)

数组还有一些常见api,还有一种特别的定型数组 操作方法 concat() slice() splice() 注意 如果 slice()的参数有负值&#xff0c;那么就以数值长度加上这个负值的结果确定位置。比 如&#xff0c;在包含 5 个元素的数组上调用 slice(-2,-1)&#xff0c;就相当于调用 slice(3,4…

【从零开始学习Java重要知识 | 第三篇】暴打ReentrantLock底层源码

目录 前言&#xff1a; 前置知识&#xff1a; 什么是公平锁与非公平锁&#xff1f; 尝试自己构造一把锁&#xff1a; ReentrantLock源码&#xff1a; 加锁&#xff1a; 解锁&#xff1a; 总结&#xff1a; 前言&#xff1a; 在并发编程中&#xff0c;线程安全是一个重…

【Python 千题 —— 基础篇】参加聚会

题目描述 题目描述 我们向五名同学(Alan、Bob、Candy、Eric、Sandy)发送了各发送了一封聚会邀请邮件,Alan、Candy 与 Sandy 都表示会参加,Bob 与 Eric 都表示不会参加;对于会参加的同学,将会回复 “I’m xxx. I’ll come to the party.”,对于不参加的同学,将会回复 …

大数据工作岗位需求分析

前言&#xff1a;随着大数据需求的增多&#xff0c;许多中小公司和团队也新增或扩展了大数据工作岗位&#xff1b;但是却对大数据要做什么和能做什么&#xff0c;没有深入的认识&#xff1b;往往是招了大数据岗位&#xff0c;搭建起基础能力后&#xff0c;就一直处于重复开发和…

C#设计模式教程(7):适配器模式

适配器模式的定义 适配器模式(Adapter Pattern)是一种结构型设计模式,它允许不兼容的接口之间能够相互合作。适配器的作用是解决那些因接口不兼容而不能一起工作的类的问题,它通过包装一个类的接口转换成另一个期望的接口。 适配器模式主要分为两种: 类适配器(Class Ad…