欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > IT业 > C++ —— Lambda 表达式

C++ —— Lambda 表达式

2025/5/20 20:03:42 来源:https://blog.csdn.net/2401_87692970/article/details/148062178  浏览:    关键词:C++ —— Lambda 表达式

在这里插入图片描述
在这里插入图片描述

🎁个人主页:工藤新一¹

🔍系列专栏:C++面向对象(类和对象篇)

🌟心中的天空之城,终会照亮我前方的路

🎉欢迎大家点赞👍评论📝收藏⭐文章


文章目录

  • Lambda
    • 一、Lambda表达式语法
    • 二、Lambda 表达式的应用
    • 三、捕捉列表
      • 3.1概念与功能描述
      • 3.2mutable
    • 四、Lambda 的原理
    • 五、Lambda 捕获悬垂引用问题

Lambda

一、Lambda表达式语法

lambda 表达式本质是一个匿名函数对象,跟普通函数不同的是他可以定义在函数内部。lambda 表达式语法使用层而言没有类型,所以我们⼀般是⽤ auto 或者模板参数定义的对象去接收 lambda 对象

  • lambda 表达式特点:轻量级
  • 快速定义一个匿名函数对象(也被称作:CLosure 闭包

lambda 表达式的格式:[capatrue-list](parameters)-> return type { function body }

  • [capatrue-list]: 捕捉列表,该列表总是出现在 [lambda] 函数的开始位置,编译器根据 [] 来判断接下来的代码是否为 lambda 函数,捕捉列表能够捕捉上下文中的变量 供 lambda 函数使用,捕捉列表可以传值和传引用捕捉(注意:即使捕捉列表为空也不能被省略)

  • (parameters): 参数列表,与普通函数的参数列表功能类似,如果不需要参数传递,那么即可连同 () 一起省略

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

  • { function body }: 函数体,函数体内的实现跟普通函数完全类似,在该函数体内,除了使用其参数外,还可使用所有捕获到的变量,函数体为空时也不能被省略。

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


二、Lambda 表达式的应用

​ 在学习 lambda 表达式之前,我们的使⽤的可调⽤对象只有 函数指针仿函数对象,函数指针的类型定义起来⽐较⿇烦,仿函数要定义⼀个类,相对会⽐较⿇烦。使用 lambda 去定义可调⽤对象,既简单又方便

lambda 在很多其他地⽅⽤起来也很好⽤,⽐如 线程 中定义线程的执⾏函数逻辑,智能指针 中定制 删除器 等,lambda 的应⽤还是很⼴泛的,以后我们会不断接触到

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


三、捕捉列表

3.1概念与功能描述

lambda 表达式中默认只能用 lambda 函数体和参数中的变量,如果想运⽤外层作⽤域中的变量则需要进行 “捕捉”

  • 第一种捕捉方式:在捕获列表中显示 传值捕获(变量只读状态)传引用捕获,捕获多个变量用逗号进行分割。[ x, y, &z ] 表示 xy 值捕获,z 引用捕获
  • 第⼆种捕捉方式:在捕捉列表中隐式捕捉,在捕捉列表写⼀个 = 表⽰隐式值捕捉(将变量全部变为值捕捉),在捕捉列表 写⼀个 & 表⽰隐式引用捕捉,这样我们 lambda 表达式中使用的那些变量,编译器就会对其进行⾃动捕捉

注意:隐式捕获,不是将程序中的所有变量都捕捉到 **lambda** 表达式中,而是需要哪个,捕获哪个

  • 第三种捕捉方式:在捕捉列表中混合使⽤隐式捕捉和显⽰捕捉, [ = , &x ] 表⽰其他变量隐式值捕捉, x 引⽤捕捉;[ &, x, y ] 表⽰其他变量引⽤捕捉,xy 值捕捉。当使⽤混合捕捉时,第⼀个元素必须是 & 或 =,并且 & 混合捕捉时,后⾯的捕捉变量必须是值捕捉,同理 = 混合捕捉时,后⾯的捕捉变量必须是引⽤捕捉。

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


  • lambda 表达式在函数局部域中,他可以捕捉 lambda 位置之前定义的变量,不能捕捉静态局部变量和全局变量,静态局部变量和全局变量也不需要捕捉,lambda 表达式中可以直接使用。这也意味着**lambda** 表达式如果定义在全局位置,捕获列表必须为空

在这里插入图片描述


3.2mutable

  • 默认情况下, lambda 捕捉列表是被 const 修饰的,也就是说传值捕捉而来的对象不能被修改,mutable 加在参数列表的后⾯可以 取消其常量性,也就说使⽤该修饰符后,传值捕捉的对象就可以被修改了,但是修改还是形参对象,不会影响实参(类似值传递,返回的是自身数据的一份临时拷贝)— — 被 lambda 通过 “传值捕获” 的内部变量,本质是外部变量的一份临时拷贝。使用 mutable 修饰符后,参数列表不可省略(即使参数不能为空)。

在这里插入图片描述


四、Lambda 的原理

lambda 的原理和 范围for 很像,编译后从汇编指令层的⻆度看,压根就没有 lambda范围for 这样的东西。范围for 底层是迭代器,⽽lambda 底层是仿函数对象,也就说我们写了⼀个 lambda 以后,编译器会⽣成⼀个对应的仿函数的类

​ 仿函数的类名是编译按⼀定规则⽣成的,保证不同的 lambda ⽣成的类名不同,lambda 参数/返 回类型/函数体就是仿函数 operator() 的参数/返回类型/函数体, lambda 的捕获列表本质是⽣成 的仿函数类的成员变量,也就是说捕获列表的变量都是 lambda 类构造函数的实参,当然隐式捕获,编译器要看使⽤哪些就传那些对象

  • 上⾯的原理,我们可以透过 汇编层 了解⼀下
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(1000, 2);auto func1 = [](){cout << "Hello World" << endl;}; func1();//lambda//捕获列表中的rate,可以视作 lambda 类构造函数的参数传递auto r2 = [rate](double money, int year){return money * rate * year;};r2(1000, 2);return 0;
}

在这里插入图片描述


  • 本质上都是给构造函数传参

在这里插入图片描述


在这里插入图片描述


  • 定义 lambda - 生成仿函数

  • 定义 lambda 对象 - 初始化仿函数对象

在这里插入图片描述


五、Lambda 捕获悬垂引用问题

在这里插入图片描述

此外,多线程中如果 捕获引用,也可能出现 引用失效 的问题,这会导致程序结果错误或访问异常等;而对于 传值捕获 则不会出现这种问题


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述

🌟 各位看官好我是工藤新一¹呀~

🌈 愿各位心中所想,终有所致!

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词