C++笔记---lambda表达式

发布时间:2026/10/24 1:42:54

C++笔记---lambda表达式 1. 简单介绍及语法Lambda表达式是C11引入的一种便捷的匿名函数定义机制。lambda 表达式本质是一个匿名函数对象跟普通函数不同的是他可以定义在函数内部。lambda 表达式语法使用层而言没有类型所以我们一般是用auto或者模板参数定义的对象去接收 lambda 对象。Lambda表达式的基本语法Lambda表达式的基本语法格式如下[capture_list](parameters) - return_type { function_body }capture_list捕获列表(不可省略)该列表总是出现在 lambda 函数的开始位置编译器根据[]来判断接下来的代码是否为 lambda 函数用于指定Lambda表达式可以访问的外部变量以及捕获的方式按值或按引用。parameters参数列表(可以省略)定义传递给Lambda表达式的参数。如果不需要参数传递则可以连同()⼀起省略- return_type可选的尾部返回类型指定(可以省略)。如果省略则由函数体中的返回语句推断返回值。function_bodyLambda表达式的函数体(不可省略)包含执行的代码函数体为空也不能省略。在该函数体内除了可以使用其参数外还可以使用所有捕获到的变量。#includeiostream using namespace std; int main() { // ⼀个简单的lambda表达式 auto add [](int x, int y)-int {return x y; }; cout add(1, 2) endl; // 1、捕捉为空也不能省略 // 2、参数为空可以省略 // 3、返回值可以省略可以通过返回对象自动推导 // 4、函数题不能省略 auto hello [] { cout hello World endl; return 0; }; hello(); int a 0, b 1; auto swap1 [](int x, int y) { int tmp x; x y; y tmp; }; swap1(a, b); cout a : b endl; return 0; }2. 捕捉列表在lambda表达式中不可以直接使用外部变量因为其本质上是一个函数要使用其所在域内的变量需要用捕捉列表进行捕捉。捕捉的方式有显式传值捕捉显式传引用捕捉隐式传值捕捉隐式传引用捕捉。显式传值捕捉与传引用捕捉第一种捕捉方式是在捕捉列表中显式的传值捕捉和传引用捕捉捕捉的多个变量用逗号分割。直接将变量写到捕捉列表中时为传值捕捉加上 则表示传引用捕捉[xyz] 表示x和y值捕捉z引用捕捉。注意传值捕捉的变量是被const修饰的不可修改。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; return 0; }隐式传值捕捉与传引用捕捉[] 捕捉列表中传入 表示自动传值捕捉在函数体内被用到的变量[]捕捉列表中传入 表示自动传引用捕捉在函数体内被用到的变量。int main() { // 只能用当前lambda局部域和捕捉的对象和全局对象 int a 0, b 1, c 2, d 3; // 隐式值捕捉 // 用了哪些变量就捕捉哪些变量 auto func2 [] { int ret a b c; return ret; }; cout func2() endl; // 隐式引用捕捉 // 用了哪些变量就捕捉哪些变量 auto func3 [] { a; c; d; }; func3(); cout a b c d endl; return 0; }混合使用显式捕捉和隐式捕捉在捕捉列表中混合使用隐式捕捉和显式捕捉[x] 表示其他变量隐式值捕捉x引用捕捉[ x y] 表示其他变量引用捕捉x和y值捕捉。当使用混合捕捉时第一个元素必须是或并且混合捕捉时后面的捕捉变量必须是值捕捉同理混合捕捉时后面的捕捉变量必须是引用捕捉。int main() { // 只能用当前lambda局部域和捕捉的对象和全局对象 int a 0, b 1, c 2, d 3; // 混合捕捉1 auto func4 [, a, b] { //a; //b; c; d; return a b c d; }; func4(); cout a b c d endl; // 混合捕捉2 auto func5 [, a, b] { a; b; /*c; d;*/ return a b c d; }; func5(); cout a b c d endl; return 0; }不能捕捉静态局部变量和全局变量lambda 表达式如果在函数局部域中他可以捕捉 lambda 位置之前定义的变量不能捕捉静态局部变量和全局变量静态局部变量和全局变量也不需要捕捉 lambda 表达式中可以直接使用。这也意味着 lambda 表达式如果定义在全局位置捕捉列表必须为空。int x 0; // 捕捉列表必须为空因为全局变量不用捕捉就可以用没有可被捕捉的变量 auto func1 []() { x; }; int main() { // 只能用当前lambda局部域和捕捉的对象和全局对象 int a 0, b 1, c 2, d 3; // 局部的静态和全局变量不能捕捉也不需要捕捉 static int m 0; auto func6 [] { int ret x m; return ret; }; return 0; }mutable 修饰符默认情况下 传值捕捉的对象是被const修饰的也就是说传值捕捉过来的对象不能修改。mutable加在参数列表的后面可以取消其常量性也就说使用该修饰符后传值捕捉的对象就可以修改了但是修改还是形参对象不会影响实参。使用该修饰符后参数列表不可省略(即使参数为空)。int main() { // 只能用当前lambda局部域和捕捉的对象和全局对象 int a 0, b 1, c 2, d 3; // 传值捕捉本质是⼀种拷被,并且被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; }3. lambda表达式的应用在学习 lambda 表达式之前我们的使用的可调用对象只有函数指针和仿函数对象函数指针的类型定义起来比较麻烦仿函数要定义一个类相对会比较麻烦。使用 lambda 去定义可调用对象既简单又方便。lambda 在很多其他地方用起来也很好用。比如线程中定义线程的执行函数逻辑智能指针中定制删除器等 lambda 的应用还是很广泛的以后我们会不断接触到。例如在下面的例子中将lambda表达式直接作为可调用对象传给sort函数不仅省去了仿函数的定义而且将比较逻辑与sort函数绑定到一起提高了可读性。struct Goods { string _name; // 名字 double _price; // 价格 int _evaluate; // 评价 // ... Goods(const char* str, double price, int evaluate) :_name(str) , _price(price) , _evaluate(evaluate) {} }; struct ComparePriceLess { bool operator()(const Goods gl, const Goods gr) { return gl._price gr._price; } }; struct ComparePriceGreater { bool operator()(const Goods gl, const Goods gr) { return gl._price gr._price; } }; int main() { vectorGoods v { { 苹果, 2.1, 5 }, { ⾹蕉, 3, 4 }, { 橙子, 2.2, 3 }, { 菠萝, 1.5, 4 } }; // 类似这样的场景我们实现仿函数对象或者函数指针支持商品中 // 不同项的比较相对还是⽐较麻烦的那么这里lambda就很好用了 sort(v.begin(), v.end(), ComparePriceLess()); sort(v.begin(), v.end(), ComparePriceGreater()); sort(v.begin(), v.end(), [](const Goods g1, const Goods g2) { return g1._price g2._price; }); sort(v.begin(), v.end(), [](const Goods g1, const Goods g2) { return g1._price g2._price; }); sort(v.begin(), v.end(), [](const Goods g1, const Goods g2) { return g1._evaluate g2._evaluate; }); sort(v.begin(), v.end(), [](const Goods g1, const Goods g2) { return g1._evaluate g2._evaluate; }); return 0; }4. lambda表达式的原理编译器在编译时会根据我们所写的lambda表达式生成一个仿函数的类并返回该类的对象。仿函数的类名是编译按一定规则生成的保证不同的 lambda 生成的类名不同lambda参数/返回类型/函数体就是仿函数operator()的参数/返回类型/函数体。lambda 的捕捉列表本质是生成的仿函数类的成员变量也就是说捕捉列表的变量都是 lambda 类构造函数的实参当然隐式捕捉编译器要看使用哪些就传那些对象。在下面这个例子中r1和r2除了类名不同以外是完全等价的。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); // lambda auto r2 [rate](double money, int year) { return money * rate * year; }; r1(10000, 2); r2(10000, 2); return 0; }我们可以使用编译器的转到反汇编的功能来看二者的底层是否一样r1(10000, 2); 00007FF71CBD1A18 mov r8d,2 00007FF71CBD1A1E movsd xmm1,mmword ptr [__real40c3880000000000 (07FF71CBDBDA8h)] 00007FF71CBD1A26 lea rcx,[r1] 00007FF71CBD1A2A call Rate::operator() (07FF71CBD1172h) 00007FF71CBD1A2F nop r2(10000, 2); 00007FF71CBD1A30 mov r8d,2 00007FF71CBD1A36 movsd xmm1,mmword ptr [__real40c3880000000000 (07FF71CBDBDA8h)] 00007FF71CBD1A3E lea rcx,[r2] 00007FF71CBD1A42 call main::2::lambda_1::operator() (07FF71CBD1EA0h) 00007FF71CBD1A47 nop可以看到二者都调用了 operator() 。5. lambda递归调用lambda表达式无法直接进行递归调用例如下面的这段计算阶乘的代码是无法通过编译的#include iostream int main() { auto fact [](int n) - int { if (n 1) return 1; return n * fact(n - 1); }; std::cout fact(5) std::endl; return 0; }这是因为lambda表达式内部不认识fact因为在编译lambda表达式的时候fact还未定义出来。fact类型的推导和值的定义依赖于lambda表达式而lambda表达式的定义也依赖于fact。5.1 借助 std::function 捕获自身引用#include functional #include iostream int main() { std::functionint(int) fact; fact [](int n) - int { if (n 1) return 1; return n * fact(n - 1); }; std::cout fact(5) std::endl; return 0; }5.2 显式对象参数C23 的新特性 ——显式对象参数Explicit Object Parameter是目前最简洁、最现代的 lambda 递归写法.核心是通过 this auto 参数 让 lambda 直接 “持有自身引用”无需依赖 std::function 语法更直观且无额外开销。#include iostream int main() { auto fact [](this auto fact, int n) - int { if (n 1) return 1; return n * fact(n - 1); }; std::cout fact(5) std::endl; return 0; }
延伸阅读

更多相关文章

2026/10/24 1:42:54

计算机基础--硬件网络(快速学习版)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/24 1:42:54

简述软件测试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/24 2:43:02

音乐与真人互动结合:Canary口语学习产品体验与设计解析

最近语言学习赛道在海外产品发现平台上很热闹,一款叫 Canary 的应用冲上了今日热榜。它的定位很有意思:用音乐学语言,靠真人互动练口语。这两个点单拎出来都不算新鲜,但放在一起做成产品,确实戳中了不少语言学习者的痛…

2026/10/24 2:43:02

数据中心月度数据更新实操复盘:从指标口径到容量预测

又到了月底整理数据中心运行数据的时间。我做企业互联网数据中心运维和容量管理已经有些年头,每月一次的“数据中心相关数据更新”看起来只是把带宽、设备、能耗、机柜使用率这些数字刷新一遍,实际上背后涉及统计口径确认、数据清洗、交叉验证、报表输出…

2026/10/24 2:43:02

TWAIN DSM源码解析:扫描链路调度与64位迁移排错

简介:这份资源是 TWAIN 数据源管理器(DSM)的开源工程源码包,对应 master 主分支的完整代码,面向需要在 Python 环境下对接扫描仪、数码相机等图像采集设备的中高级开发者。它解决了跨 32 位与 64 位平台调用 TWAIN 接口…

2026/10/24 2:43:02

动漫交流与推荐平台实战:从数据库设计到推荐算法落地

做动漫交流与推荐平台这个项目,前后花了大概一个半月,中途推翻了两次设计方案。最初接到这个需求时,我并没有急着写代码,而是先想清楚一个问题:市面上的动漫站点少说也有几十个,用户凭什么用我们这套平台&a…

2026/10/24 2:43:02

C# VISA仪器控制:统一USB/GPIB/LAN的SCPI会话实战

简介:面向自动化测试与数据采集场景的 C# 仪器控制入门实践包,重点介绍如何借助 VISA 标准接口,统一调用示波器、信号发生器、数字万用表等不同厂商的设备,适合已掌握 C# 基础、刚开始接触仪器通信的开发者。压缩包共 15 个文件、…

2026/10/24 2:38:02

U盘设备指纹:VID、PID、盘符与物理序列号获取实战

简介:这份源码资源面向需要在 Windows 平台读取 U 盘、移动硬盘、手机存储卡及 MP3/MP4 等移动存储设备硬件信息的开发者,重点解决如何获取设备 VID、PID、盘符与物理序列号的问题。作者在论坛资料基础上整理实现,最终可输出类似「盘符\VID_0…

2026/10/23 22:56:35

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/24 0:02:40

MATLAB缩进快捷键与批量格式化实战指南

1. 被自动缩进折磨的那些年如果你写过MATLAB代码,大概率经历过这种场景:从某个文档、网页或者聊天记录里复制了一段代码,粘贴进编辑器之后,每一行前面都带着莫名其妙的空格,有的行首是四个空格,有的是一个T…

2026/10/24 0:02:40

用Poco实现Qt风格信号与槽的跨平台C++方案

简介:本资源是一份面向C中级开发者的技术实践工程,聚焦于在非Qt框架环境下模拟实现Qt核心通信机制——信号与槽,借助Poco网络库的事件系统完成解耦式对象通信。适用于需在跨平台C项目中引入松耦合回调机制、但受限于无法集成Qt依赖的开发场景…

2026/10/24 0:02:40

ESXi裸机虚拟化安装指南:从硬件兼容到虚拟机部署

1. 为什么还要折腾ESXi:从一台闲置机器说起手里攒了几台淘汰下来的迷你主机,配置不算差,i5加16G内存,扔了可惜,装个桌面系统又觉得浪费。这种场景下,ESXi就成了一个绕不开的选择。它是一套裸机虚拟化平台&a…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑