C++ lambda表达式及其原理

2023-05-16

概述

C++ 11中引入了新的lamdba表达式,使用也很简单,我最喜欢的是不用给函数取名称,每次给函数取名称都感觉自己读书太少~

1、lambda表达式

lambda表达式可以理解为一个匿名的内联函数。和函数一样,lambda表达式具有一个返回类型、一个参数列表和一个函数体。与函数不一样的是lambda必须使用尾置返回类型。一个lambda表达式表示一个可调用的代码单元。

语法:[capture list] (parameter list) -> return type {function body}

capture list:表示捕获列表,是一个lambda所在函数中定义的局部变量列表
parameter list:表示参数列表
return type:返回类型
function body:函数体

我们可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体,忽略参数列表等价于指定一个空函数列表,忽略返回类型,lambda会根据函数体中的代码推断出来(如果函数体直接return,则是void类型)。例如:

auto f = [ ] {return 42;};
cout << f() << endl;

lambda的调用方式与普通函数的调用方式相同。

与函数的几点不同在于:

  1. lambda表达式不能有默认参数。因此,一个lambda表达式调用的实参数目永远与形参数目相等
  2. 所有参数必须有参数名
  3. 不支持可变参数

2、捕获列表

如果没有进行捕获,则lambda表达式函数体内只能使用参数列表中的变量。捕获就是明确的指明lambda能使用的局部变量(指调用lambda的地方局部变量)。

这里记住两点就行了:

  1. lambda只有在其捕获列表中捕获一个它所在函数中的局部变量,才能在函数体中使用该变量
  2. 捕获列表只用于非静态局部变量,lambda可以直接使用静态局部变量和在它所在函数之外声明的名字

例如:

void func()
{
	static int i = 10;
	int  j = 20;
	auto f1 = [ ] () { return j; };		//编译出错,没有进行捕获,函数体内不能使用
	auto f2 = [ ] () { return i; };		//静态变量无需捕获
	auto f3 = [j] () { return j; };		//进行了捕获,函数体内可以使用了
};

与函数参数传递类似,变量的捕获方式也可以是值或者引用。这里列出不同的捕获列表的方式,后面进行解释:

在这里插入图片描述

2.1、值捕获

与传递参数类似,采用值捕获的前提是变量可以拷贝。与参数不同,不捕获的变量的值是在lambda创建时拷贝,而不是调用时拷贝。例如:

void func()
{
	size_t v1 = 42;
	auto f = [ v1 ]  { return v1; };	//使用了值捕获,将v1拷贝到名为f的可调用对象
	v1 = 0;
	auto j = f();	//j为42,f保存了我们创建它是v1的拷贝
}

由于被捕获的值实在lambda创建时拷贝,因此在随后对其修改不会影响到lambda内部对应的值。

默认情况下:如果以传值方式捕获外部变量,则在Lambda表达式函数体中不能修改该外部变量的值

2.2、引用捕获

和函数引用参数一样,一个引用类型的变量在函数体内改变时,实际上使用的是引用所绑定的对象。

void func()
{
	size_t v1 = 42;
	auto f = [ &v1 ]  { return v1; };	//引用捕获,将v1拷贝到名为f的可调用对象
	v1 = 0;
	auto j = f();	//j为42,f保存了我们创建它是v1的拷贝
}

如果我们采用引用方式捕获一个变量,就必须确保被引用的对象在lambda执行的时候是存在的。lambda捕获的都是局部变量,这些变量在函数结束后就不复存在了。如果lambda可能在函数结束后执行,这里就会出现问题。

有一些不可拷贝对象,只能使用引用捕获的方式,比如ostream对象。

2.3、隐式捕获

除了显示列出我们希望使用的来自所在函数的局部变量之外,我们还可以让编译器根据函数体中的代码来推断需要捕获哪些变量,这种方式称之为隐式捕获

隐式捕获有两种方式,分别是[=]和[&]。[=]表示以值捕获的方式捕获外部变量,[&]表示以引用捕获的方式捕获外部变量

int main()
{
    int a = 123;
    auto f = [ = ]  { cout << a << endl; };		//值捕获
    f(); 	// 输出:123
    
    auto f1 = [ & ] { cout << a++ << endl; }; 		//引用捕获
    f1();	//输出:123(采用了后++)
    
    cout << a << endl; 		//输出 124
}

2.4、混合方式捕获

lambda还支持混合方式捕获,即同时使用显示捕获和隐式捕获

混合捕获时,捕获列表中的第一个元素必须是 = 或 &,此符号指定了默认捕获的方式是值捕获或引用捕获

需要注意的是:显示捕获的变量必须使用和默认捕获不同的方式捕获。例如:

void func()
{
	int i = 10;
	int  j = 20;
	auto f1 = [ =, &i] () { return j + i; };		//正确,默认值捕获,显示是引用捕获
	auto f2 = [ =, i] () { return i + j; };		//编译出错,默认值捕获,显示值捕获,冲突了
	auto f3 = [ &, &i] () { return i +j; };		//编译出错,默认引用捕获,显示引用捕获,冲突了
};

2.5、修改值捕获的值

在Lambda表达式中,如果以传值方式捕获外部变量,则函数体中不能修改该外部变量,否则会引发编译错误。

如果你希望被值捕获的值被改变,就必须在参数列表首加上关键字mutable

语法变为:[capture list] (parameter list) mutable -> return type {function body}

int main()
{
    int a = 123;
    auto f = [a]()mutable { cout << ++a; }; // 不会报错
    
    cout << a << endl; 	// 输出:123
    f(); 					// 输出:124
}

3、返回类型

在默认的规则下,返回类型如下:

  1. 如果只包含单一的return语句,那么根据return 的类型确定返回类型。
  2. 如果除了return 还有别的语句,那么返回void。

所以,有返回类型时,一定要自己显示的进行说明。

4、lambda表达式的本质

当我们编写了一个lambda之后,编译器将该表达式翻译成一个未命名类的未命名对象该类含有一个重载的函数调用运算符

4.1、采用值捕获

在采用值捕获时,lambda形成的类相当于含有自己的数据成员,同时创建构造函数,令其使用捕获的变量的值来初始化数据成员。例如两个数加法的方法:

int func()
{	
	int a =10;
	int b = 20;
	auto addfun = [=] (const int c ) -> int { return a+c; };
	
	int c = addfun(b);    
	cout << c << endl;
};

就等同于:

class Myclass
{
public:
	Myclass( int a ) : m_a(a){};	//该形参对应捕获的变量
	
	//该调用运算符的返回类型、形参和函数体都与lambda一致
	int operator()(const int c) const
	{
		return a + c;
	}
	
private:
	int m_a;		//该数据对应通过值捕获的变量
};

lambda表达式产生的类不含有默认构造函数、赋值运算符及默认析构函数。因为不含默认构造函数,因此要想使用这个类必须提供一个实参。

默认情况下,由lambda产生类当中的调用运算符是一个const成员函数,所以值捕获的值不能修改。如果加上mutable相当于去掉const。这样上面的很多限制就能讲通了。

4.2、采用引用捕获

如果lambda采用引用捕获的方式,编译器可以直接使用该引用而无须在lambda对象产生的类中将其存储为数据成员。

唯一需要注意的是,变量将由程序负责确保执行时引用的对象确实存在。

感谢大家,我是假装很努力的YoungYangD(小羊)

参考资料:
《C++ primer 第五版》
https://www.cnblogs.com/lustar/p/7531605.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ lambda表达式及其原理 的相关文章

  • lambda 始终返回“1”

    有这样的代码 include
  • C# Elasticsearch NEST 无法转换 lambda 表达式

    我遇到了与此处描述的完全相同的问题 但未得到解答 ElasticSearch NEST 搜索 https stackoverflow com questions 24615676 elasticsearch nest search I us
  • 使用 linq 组合对象

    我有一个类的 2 个实例 它实现了IEnumerable界面 我想创建一个新对象并将它们合并为一个 我明白我可以使用for each去做这个 有 linq lambda 表达式的方法可以做到这一点吗 EDIT public class Me
  • “System.Int32”类型的表达式不能用于返回类型“System.Object”

    我正在尝试制作一个简单的脚本系统 用于打印标签 我过去曾通过反射完成此操作 没有任何问题 但我现在尝试使用 Lambda 函数来完成此操作 以便可以缓存函数以供重用 到目前为止我的代码如下 public static string GetV
  • 如何将这段 javascript 代码重写为 C++11?

    这是我在 Javascript Definitive Guide 中看到的 javascript 闭包代码 我想把它写成C 11 var uniqueID1 function var id 0 return function return
  • lambda 表达式是多线程的吗?

    lambda 表达式是多线程的吗 假设当你将数学公式编写为 lambda 方法时 当你将其传递给另一个方法时 它会是多线程的吗 不是100 清楚你问的是什么 您是否想问 lambda 是否自然地在不同的线程上运行 如果是这样 则它们只是 S
  • 使用 lambda 对多列进行分组

    如何使用 lambda 对多列进行分组 我看到了如何使用 linq toEntity 执行此操作的示例 但我正在寻找 lambda 形式 var query source GroupBy x gt new x Column1 x Colum
  • 如何从 pandas 数据框中的列中删除字符串值

    我正在尝试编写一些代码 以逗号分隔数据帧列中的字符串 因此它成为一个列表 并从该列表中删除某个字符串 如果存在 删除不需要的字符串后 我想再次以逗号加入列表元素 我的数据框如下所示 df Column1 Column2 0 a a b c
  • FindAll 与Where 扩展方法

    我只想知道 FindAll 是否比 Where 扩展方法更快 为什么 例子 myList FindAll item gt item category 5 or myList Where item gt item category 5 哪个更
  • 嵌套字段的 Comparator.comparing(...)

    假设我有一个这样的域模型 class Lecture Course course getters class Course Teacher teacher int studentSize getters class Teacher int
  • lambda 表达式的“类型”可以表达吗?

    将 lambda 表达式视为可调用对象的 语法糖 是否可以表达未命名的基础类型 一个例子 struct gt bool operator int l int r return l gt r Now int l int r return l
  • 如何确定 std::function 的参数数量?

    我有以下问题 假设您想编写一个可以采用 lambda 表达式的通用函数 我知道如果参数是 std function 类型 那么我不仅可以使用 lambda 还可以使用函数 甚至可以使用函数指针 所以第一步 我做了以下事情 void prin
  • Haskell - lambda 表达式

    我试图了解什么是有用的以及如何在 Haskell 中实际使用 lambda 表达式 我不太明白使用 lambda 表达式相对于定义函数的约定方式有何优势 例如 我通常会执行以下操作 let add x y x y 我可以简单地打电话 add
  • 过度使用委托对性能来说是一个坏主意吗? [复制]

    这个问题在这里已经有答案了 考虑以下代码 if IsDebuggingEnabled instance Log GetDetailedDebugInfo GetDetailedDebugInfo 可能是一个昂贵的方法 因此我们只想在调试模式
  • Java 8 lambda 和抽象类的接口扩展

    说我想宣布Spring的RowMapper 但不是创建动态类 而是实现一个实现 RowMapper 的抽象类 这是我的方法签名 SqlProcedure declareRowMapper RowMapper
  • 基于 AST 结合使用 And Or 和 Not 表达式的 C# 表达式

    我想使用 Linq 表达式来实现一些动态功能 我需要 And Or 和 Not 表达式 我无法得到太多 我们想要检查系统中是否启用了某些功能 并据此决定是否显示菜单项 我们已经形成了 XML 格式的规则 我知道将规则转换为 AST 但我不知
  • 在这种情况下,我如何处理 Function 和省略号/可变参数?

    我的项目之一是抛出 lambda 表达式 https github com fge throwing lambdas 我的目标是简化潜在的使用 FunctionalInterfaces in Streams 其在流中使用的唯一 缺陷 是它们
  • 为什么《小阴谋家》中的所有 lambda 表达式都是如此?

    在从 SICP 学习了一些计划之后 我开始阅读 小计划 我觉得这本书很有趣 并且已经完成了大约四分之一 我注意到我可以在不使用 lambda 的情况下编写许多 大多数 全部 解决方案 而 The Little Scheduleralways
  • 创建 lambda 二维数组

    因此 我搜索了几个网站和其他有关此问题的问题 但似乎没有一个答案适合我 我有有效的代码 我的编程导师建议我更改链接if else if改为使用 lambda 表 我询问是否使用某种哈希表 他说仅对 9 个项目使用哈希 真正的程序有 9 个
  • Lambda 按值捕获和“mutable”关键字

    关键词的必要性mutable在 lambda 中 是造成极大混乱的根源 考虑代码 int x 10 function

随机推荐

  • arduino的串口缓冲区_arduino串口通信

    1 串口设置 arduino串口通信 Serial begin 说明 开启串口 xff0c 通常置于setup 函数中 语法 Serial begin speed Serial begin speed xff0c config 参数 spe
  • linux映像文件结构,ROMFS文件系统分析[三]ROMFS映像文件结构

    前面说了我们使用genromfs工具可以生成romfs文件系统 xff0c 那其生成的映像的格式是什么 xff1f 这就要探究romfs文件系统的本质了 1 romfs映像结构使用genromfs生成的romfs格式映像中 xff0c 文件
  • 依据imu姿态角计算z轴倾角_树莓派小车-07-IMU姿态解算 imu_complementray_filter

    上一篇文章介绍了互补滤波器与ROS的接口定义 xff0c 这篇文章将结合论文分析代码 complementary filter cpp 首先从成员变量开始看 xff0c 毕竟这些变量在后面用到的时候需要了解他所代表的意思 xff0c 同时也
  • android 动画 三色流动,Android 三色状态指示进度条 - ThreeColorIndicator

    ThreeColorIndicator 这是一个 Android 三色状态指示进度条 xff0c 常用于指示 xff1a 信号强度 温度等 xff0c 可通过文字 颜色表示一个值的好 一般 差 xff0c 也可以自定义为其它状态 预览图 使
  • stm32串口接收不定长数据_串口发送数据的验证 不定长度多字节的处理4

    最后遗留的一个问题 xff0c 在Modbus RTU的读取功能中就完美解决了 灵光一闪 发送帧的第5字节数据就要要读取的长度 xff0c 响应帧的第3字节数据就是返回数据的长度 xff0c 后面的字节就是返回数据 因为1个寄存器数据是2个
  • m3u直播源_M3U播放列表生成工具

    来源 xff1a 黑鸟博客 快速制作支持 VLC 和 Potplayer 等播放器的 XSPF DPL M3U 等播放列表格式的工具 xff0c 并且可以查重复 xff0c 自定义设置多种选项 所有的播放列表都可以使用普通的文本编辑器 xf
  • js字符串包含另一个字符串_C语言中,在一个字符串中插入另一个字符串(字符)...

    本题可以看做一个字符串拼接问题 需要一个载体数组 includevoid insert array char s1 char s2 int n 思路 1 得到主串s1和子串s22 找到插入位置 3 进行插入 void main char s
  • 华为手机一键解锁工具箱下载 | 华为手机解BL锁软件: 支持解锁bootloader,刷写recovery功能

    文章目录 1 软件介绍2 特色功能3 资源站点4 下载地址5 软件截图6 安装教程7 使用教程7 1 解锁BL 1 软件介绍 通过这款华为手机实用工具箱可以对你的华为手机系列进行刷机 解锁等操作 xff0c 网上这种华为刷机解锁工具比较少
  • python subprocess 实时输出_Python标准库初探之subprocess

    一 subprocess简介 人生苦短 xff0c 我用Python 今天给大家带来一个在Python脚本中启动进程的利器 subprocess 人们都说Python是一个胶水语言 xff0c 可以方便地在多平台上调用其他指令 xff0c
  • 进程内存中堆和栈的区别

    1 概述 在整理数据结构时 xff0c 整理过栈 队列和堆 xff0c 但是在学习进程分布的时候又碰到了 栈和堆 xff0c 初学时很容易把这几个概念给弄混 xff0c 今天有空就给整理一下 2 程序在内存中的分布 程序在内存中的分布如下图
  • C++ Mutable

    1 mutable 含义及常规使用 mutable 英文中表示 xff0c 易变的 xff0c 不定的 xff1b 性情不定的 xff0c 而在代码中表示 可变数据成员 由前面整理的 const详解 知道 xff0c 由const修饰的成员
  • 牛吃草问题

    1 概述 最近碰到一个面试题 xff0c 讲的是牛吃草的问题 xff0c 当时时间短 xff0c 脑袋出现了短路 xff0c 没有给出答案 回来特意查了一下答案 xff0c 发现了一篇比较好的文章 xff0c 现在重新抄写一份 xff0c
  • 开始记录学习中的点滴

    随着年龄的增长 xff0c 除了去了很多地方之外 xff0c 感觉个人没有特别明显的成长 xff0c 对于未来充满了更多的迷茫与困惑 对于程序员的我来说更是感觉到了自己的瓶颈 xff0c 知识储备没有增加多少 xff0c 随着时间的流逝 x
  • C++中Struct与Class的区别与比较

    概述 之前只知道在C 43 43 中类和结构体的区别只有默认的防控属性 xff08 访问控制 xff09 不同 xff0c struct是public的 xff0c 而class是private的 但经过上网查资料才发现 xff0c 除了这
  • 函数调用约定的详解

    概述 在工作的过程中 xff0c 我们总是需要调用底层函数或者使用第三方的库 xff0c 在使用的过程中我就发现了有一些函数前面总有一些 stdcall xff0c 之初我只知道那是调用约定 xff0c 但别人问我什么是调用约定 xff0c
  • #pragma的常用方法讲解

    概述 我们在写代码时 xff0c 总会遇到头文件多次包含的情况 xff0c 刚开始时我们使用宏定义进行控制 xff0c 之后发现有 pragma once这样简单的东西 xff0c 当时是很兴奋 xff0c 以为 pragma就这一种用法
  • C++数组的详细解析

    概述 数组在写程序时经常用到 xff0c 但是对于它和指针的关系 xff0c 自己经常搞混 xff0c 所有抽点时间对数组进行整理 1 数组的概念和使用 数组是用来存储相同类型的变量的顺序集合 所有的数组都是由连续的内存位置组成 最低的地址
  • 华为荣耀9升降级系统 | 华为荣耀9变砖后如何救砖 | 华为荣耀9获取BL解锁码以及如何解BL锁 | 华为荣耀9如何通过写ramdisk.img来获取root

    文章目录 1 按2 通过官方华为手机助手升降级以及修复系统和安装驱动3 使用百分之五模式刷高维禁用包355来安装指定的系统版本8 0 0 3554 故意 xff08 或意外 xff09 刷错包把手机变砖5 使用救砖模式刷高维禁用包355来安
  • C++指针详解

    概述 C C 43 43 语言之所以强大 xff0c 以及其自由性 xff0c 很大部分体现在其灵活的指针运用上 因此 xff0c 说指针是C C 43 43 语言的灵魂一点都不为过 有好的一面 xff0c 必然会有坏的一面 xff0c 指
  • C++ lambda表达式及其原理

    概述 C 43 43 11中引入了新的lamdba表达式 xff0c 使用也很简单 xff0c 我最喜欢的是不用给函数取名称 xff0c 每次给函数取名称都感觉自己读书太少 1 lambda表达式 lambda表达式可以理解为一个匿名的内联