当前位置: 首页 > news >正文

【C++】简单介绍lambda表达式

在这里插入图片描述
各位大佬好,我是落羽!一个坚持不断学习进步的学生。
如果您觉得我的文章还不错,欢迎多多互三分享交流,一起学习进步!

也欢迎关注我的blog主页: 落羽的落羽

文章目录

  • 一、 什么是lambda表达式
  • 二、 表达式语法
  • 三、lambda的应用

一、 什么是lambda表达式

C++中有“可调用对象”的概念,主要包括函数指针、仿函数、lambda表达式。lambda表达式本质是一个匿名函数对象,跟普通函数不同的是,他可以定义在函数内部

lambda表达式在语法使用层面上“没有类型”,简单来说:lambda表达式的类型是编译器即时生成的、唯一的、匿名的,程序员无法在代码中直接写出它的类型名称。lambda表达式使用时本质是被转换成一个仿函数再使用的,每个lambda表达式都会被编译器转换成一个独一无二的、匿名的类(仿函数)。即使两个lambda看起来完全一样,编译器也会为它们生成两个不同的类。所以,我们一般都是用auto或者模板参数定义的对象去接收lambda对象

二、 表达式语法

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

  • (parameters):参数列表。与普通的函数参数列表功能类似,如果不需要传参,则可以连同()一起省略。
  • ->return-type:return type是lambda函数的返回值类型。如果没有返回值,那么->return type这部分就可以省略,或者返回值类型明确的情况下,能由编译器自动推导返回类型出来,也能省略这部分。
  • {function body}:函数体。函数体的实现与普通函数完全一样,函数体内除了能使用参数列表外,还可以使用捕捉列表内的变量。函数体为空也必须写{}
  • [capture-list]:捕捉列表。该列表写在lambda表达式的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数。捕捉列表能够捕捉上下文的变量,以供lambda函数内部使用,捕捉列表可以传值或传引用,捕捉列表就算为空也必须写[]

举几个栗子:

auto add = [](int x, int y){ return x + y; };

在这里插入图片描述

auto func = []{cout << "hello world" << endl;};

在这里插入图片描述

auto swap = [](int& a, int& b){int tmp = a;a = b;b = tmp;};

在这里插入图片描述
非常好理解吧!

关于捕捉列表,还有一些使用的细节:

lambda表达式中默认只能使用参数列表中的变量或lambda内定义的,如果想使用外层作用域中的变量就需要进行捕捉。

  • 第一种方式是显式捕捉,在捕捉列表中可以显式传值捕捉和传引用捕捉,捕捉的多个变量用逗号分隔,比如[x, y, &z]表示x和y进行传值捕捉,lambda内部改变x和y不会改变原变量,而z是传引用捕捉,对z的改变会改变原变量。
  • 第二种捕捉方式是隐式捕捉,我们在[ ]中写一个 = 表示隐式值捕捉,写一个 & 表示隐式引用捕捉。这样我们的lambda的表达式中使用了哪些外部变量,编译器就会自动捕捉那些变量。
  • 第三种捕捉列表是混合捕获,可以组合使用隐式捕获和显式捕获,实现更精细的控制。[=, &x] 表示x进行引用捕捉,其余变量都为值捕捉;[&, x, y] 表示x和y进行值捕捉,其余变量进行引用捕捉。使用混合捕捉时,第一个元素必须是=或&,第一个是=时,后面的捕捉变量必须是引用捕捉;第一个是&时,后面的捕捉变量必须是值捕捉。

lambda表达式中如果在局部域时,可以捕捉到它之前定义的变量,不能捕捉静态局部变量和全局变量,因为这两种变量本来就不需要捕捉也能在内部直接使用,lambda表达式内部可以直接使用。lambda表达式如果定义在全局位置,捕捉列表必须为空。

默认情况下,lambda捕捉列表是const属性的,也就是说传值捕捉来的对象不能被修改,在参数列表后加上修饰符mutable就可以取消其常性,但是修改还是改变的形参对象,不会影响实参。使用该修饰符后参数列表不能省略。

int x = 0;
// 捕捉列表必须为空,因为全局变量不⽤捕捉就可以⽤,没有可被捕捉的变量
auto func1 = [](){x++;};int main()
{// 只能⽤当前lambda局部域和捕捉的对象和全局对象int a = 0, b = 1, c = 2, d = 3;auto func1 = [a, &b]{// 值捕捉的变量不能修改,引⽤捕捉的变量可以修改//a++;b++;int ret = a + b;return ret;};cout << func1() << endl;// 隐式值捕捉// ⽤了哪些变量就捕捉哪些变量auto func2 = [=]{int ret = a + b + c;return ret;};cout << func2() << endl;// 隐式引⽤捕捉// ⽤了哪些变量就捕捉哪些变量auto func3 = [&]{a++;c++;d++;};func3();cout << a << " " << b << " " << c << " " << d << endl;// 混合捕捉1auto func4 = [&, a, b]{//a++;//b++;c++;d++;return a + b + c + d;};func4();cout << a << " " << b << " " << c << " " << d << endl;// 混合捕捉1auto func5 = [=, &a, &b]{a++;b++;/*c++;d++;*/return a + b + c + d;};func5();cout << a << " " << b << " " << c << " " << d << endl;// 局部的静态和全局变量不能捕捉,也不需要捕捉static int m = 0;auto func6 = []{int ret = x + m;return ret;};// 传值捕捉本质是⼀种拷⻉,并且被const修饰了// mutable相当于去掉const属性,可以修改了// 但是修改了不会影响外⾯被捕捉的值,因为是⼀种拷⻉auto func7 = [=]()mutable{a++;b++;c++;d++;return a + b + c + d;};cout << func7() << endl;cout << a << " " << b << " " << c << " " << d << endl;return 0;
}

三、lambda的应用

学习lambda表达式之前,我们使用的可调用对象只有函数指针和仿函数,它们的定义都相对麻烦,使用lambda表达式去定义可调用对象,就十分简单方便了。

vector<pair<int, int>> v = { {1,2}, {3,4}, {8,3}, {5,1} };
//遇到这样的场景,假如我们想要用多种排序规则进行排序,以前是需要写不同的仿函数传给sort/*struct Compare_first
{bool operator()(const pair<int, int>& p1, const pair<int, int>& p2){return p1.first < p2.first;}
}; 
struct Compare_second
{bool operator()(const pair<int, int>& p1, const pair<int, int>& p2){return p1.second < p2.second;}
};
sort(v.begin(), v.end(), Compare_first());
sort(v.begin(), v.end(), Compare_second());*///有了lambda表达式后,就方便许多了
sort(v.begin(), v.end(), [](const pair<int, int>& p1, const pair<int, int>& p2) {return p1.first < p2.first; });
for (auto pa : v)
{cout << pa.first << ":" << pa.second << " ";
}
cout << endl;sort(v.begin(), v.end(), [](const pair<int, int>& p1, const pair<int, int>& p2) {return p1.second < p2.second; });
for (auto pa : v)
{cout << pa.first << ":" << pa.second << " ";
}
cout << endl;

在这里插入图片描述

本篇完,感谢阅读。


文章转载自:

http://cBkG3O65.dhpjq.cn
http://kC6mqvRZ.dhpjq.cn
http://cMyiBwjG.dhpjq.cn
http://GXxxr0qq.dhpjq.cn
http://nfXjMN8y.dhpjq.cn
http://g92JNAMm.dhpjq.cn
http://vwn0xxm2.dhpjq.cn
http://I7daGPHn.dhpjq.cn
http://Jcu2WMKs.dhpjq.cn
http://FEr3FQAf.dhpjq.cn
http://CZs0YJY6.dhpjq.cn
http://aq9VbEy2.dhpjq.cn
http://fdLQme76.dhpjq.cn
http://DjmDhIy8.dhpjq.cn
http://vNkBsqV6.dhpjq.cn
http://OdSFf4SX.dhpjq.cn
http://ccjAS8CX.dhpjq.cn
http://Kz5btMI1.dhpjq.cn
http://BANe3oQP.dhpjq.cn
http://MhmR1j4Q.dhpjq.cn
http://0Ycc3gOf.dhpjq.cn
http://TySd6iEc.dhpjq.cn
http://iN9LVbnp.dhpjq.cn
http://a9vxO2sS.dhpjq.cn
http://DGPGSFu1.dhpjq.cn
http://BIujaypq.dhpjq.cn
http://KWIc1zsV.dhpjq.cn
http://HW5MTX6O.dhpjq.cn
http://S6pduZIL.dhpjq.cn
http://OaNsuH5x.dhpjq.cn
http://www.dtcms.com/a/371352.html

相关文章:

  • uv 包管理器:优势解析与使用指南
  • Android studio的adb和终端的adb互相抢占端口
  • 同类软件对比(四):Jupyter vs PyCharm vs VS Code:Python开发工具终极选择指南
  • 【MySQL】数据库的基本操作
  • PaddlePaddle——飞桨深度学习实现手写数字识别任务
  • Docker Compose 运行 Milvus (Mac) 并与 python 连接测试
  • 03-Redis 安装指南:从版本选择到多系统部署(Windows+macOS+Linux)
  • 路由策略实验配置
  • 【高并发内存池】五、页缓存的设计
  • PHP - OPcache 字节码缓存 - 学习/实践
  • redis学习——七
  • nginx反向代理不转发静态文件的解决办法
  • Webassemly和声明宏的联合使用
  • 选拔考试复现
  • 【Linux】 进程控制
  • C++ 连接 Redis:redis-plus-plus 安装与使用入门指南
  • K8s访问控制(二)
  • PerfTest:轻量高性能压测工具,兼容 HTTP/1/2/3、WebSocket,并带实时监控
  • 【Linux基础】fdisk命令详解:从入门到精通的磁盘分区管理完全指南
  • 【从零开始学习Redis】秒杀优化——阻塞队列、消息队列实现异步秒杀
  • 【基于深度学习的中草药识别系统】
  • AI 驱动数据分析:开源 SQLBot 项目探索,基于大模型和 RAG 实现精准问数与图表挖掘
  • 延迟 队列
  • 宋红康 JVM 笔记 Day14|垃圾回收概述
  • 【ICCV2025】计算机视觉|即插即用|ESC:颠覆Transformer!超强平替,ESC模块性能炸裂!
  • 手机能看、投屏 / 车机不能看与反向链接验证类似吗?
  • Xilinx ZYNQ 开发环境中搭建 Qt 环
  • leetcode909.蛇梯棋
  • JAVA NIO学习笔记基础强化学习总结
  • 基于51单片机手机无线蓝牙APP控制风扇调速设计