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

C++ Lambda表达式:从入门到精通

引言

在前面 STL 系列中,我们大量使用了这样的写法:

sort(v.begin(), v.end(), [](int a, int b) { return a > b; });

这里的[](int a, int b) { return a > b; }就是Lambda 表达式。它是 C++11 引入的核心特性之一,本质是一个匿名的仿函数对象。相比传统仿函数需要单独定义一个类,Lambda 可以在使用时直接写出逻辑,代码更紧凑、可读性更高。

Lambda 在现代 C++ 中几乎无处不在——STL 算法、回调函数、线程创建、异步编程……掌握 Lambda 是写出优雅 C++ 代码的必备技能。

第一部分:Lambda 的基本语法

一、完整语法结构

五个部分可以省略其中几个,最简单的 Lambda 可以写成[]{}

部分作用可否省略
[capture]捕获外部变量❌ 不能省
(parameters)参数列表✅ 无参可省
mutable允许修改按值捕获的变量
-> return_type显式指定返回类型✅ 可自动推导
{ body }函数体❌ 不能省

二、从简单到复杂

#include <iostream> using namespace std; int main() { // 1. 最简单的 Lambda:无参无返回值 [] { cout << "Hello Lambda" << endl; }(); // 2. 带参数 [](int a, int b) { cout << a + b << endl; }(3, 5); // 8 // 3. 有返回值(自动推导) auto add = [](int a, int b) { return a + b; }; cout << add(3, 5) << endl; // 8 // 4. 显式指定返回类型 auto divide = [](double a, double b) -> double { return a / b; }; cout << divide(5.0, 2.0) << endl; // 2.5 return 0; }

返回类型推导规则:如果函数体只有一条return语句,编译器会自动推导返回类型。如果有多条return且类型不同,必须用->显式指定。


第二部分:捕获列表

捕获列表是 Lambda 最核心、区别于普通函数的关键特性。

一、值捕获 vs 引用捕获

int a = 10, b = 20; // 值捕获:拷贝一份,Lambda 内修改不影响外部 auto f1 = [a, b] { return a + b; }; // a = 100; // 不影响 f1 内部的 a(已经拷贝了) // 引用捕获:Lambda 内修改会影响外部 auto f2 = [&a, &b] { a = 100; // 修改了外部的 a b = 200; }; f2(); cout << a << " " << b << endl; // 100 200

二、捕获方式总览

写法含义示例
[x]值捕获 x[x]{ return x; }
[&x]引用捕获 x[&x]{ x = 10; }
[=]值捕获所有外部变量[=]{ return a + b; }
[&]引用捕获所有外部变量[&]{ a = 10; b = 20; }
[=, &x]默认值捕获,x 引用捕获[=, &x]{ x++; return a; }
[&, x]默认引用捕获,x 值捕获[&, x]{ return x; }
[this]捕获当前对象的 this 指针[this]{ return this->val; }
int a = 1, b = 2, c = 3; // = 默认值捕获所有 auto f1 = [=] { return a + b + c; }; // 拷贝 a, b, c // & 默认引用捕获所有 auto f2 = [&] { a = 10; b = 20; }; // 引用 a, b, c // 混合:默认值捕获,但 c 引用捕获 auto f3 = [=, &c] { // a, b 是拷贝,c 是引用 c = 100; return a + b + c; };

三、捕获的时机

int x = 10; // Lambda 定义时捕获,不是调用时捕获! auto f = [x] { return x; }; x = 100; cout << f() << endl; // 10(不是 100!定义时 x=10 已被拷贝)

第三部分:mutable 关键字

按值捕获的变量在 Lambda 内默认是只读的。要修改它们,需要mutable

int x = 10; // ❌ 错误:按值捕获的变量是只读的 // auto f1 = [x] { x++; return x; }; // ✅ 加 mutable 后可以修改(不影响外部变量) auto f2 = [x]() mutable { x++; return x; }; cout << f2() << endl; // 11 cout << f2() << endl; // 12(x 是 Lambda 内部的副本,会累积) cout << x << endl; // 10(外部 x 不受影响)

mutable的本质:去掉 Lambda 的operator()上的const修饰符。


第四部分:Lambda 的本质

Lambda 本质上是编译器自动生成的匿名仿函数类

auto f = [x](int a) { return a + x; }; // 编译器大致会生成这样的类: class __Lambda_12345 { int x; // 捕获的变量成为成员变量 public: __Lambda_12345(int val) : x(val) {} int operator()(int a) const { return a + x; } };

这意味着

  • Lambda 是一个对象,有自己的内存空间

  • 按值捕获的变量存储在 Lambda 对象内部

  • Lambda 可以像对象一样被复制、传递、存储

    // Lambda 作为函数参数(最常见) sort(v.begin(), v.end(), [](int a, int b) { return a > b; }); // Lambda 赋值给变量 auto cmp = [](int a, int b) { return a > b; }; sort(v.begin(), v.end(), cmp); // Lambda 存储在容器中 vector<function<bool(int,int)>> funcs; funcs.push_back([](int a, int b) { return a > b; });

    第五部分:常用场景

    一、STL 算法(最常见)

    #include <algorithm> #include <vector> using namespace std; vector<int> v = {5, 2, 8, 1, 9, 3, 2, 5}; // sort 自定义比较 sort(v.begin(), v.end(), [](int a, int b) { return a > b; }); // find_if 条件查找 auto it = find_if(v.begin(), v.end(), [](int x) { return x > 5; }); // count_if 条件统计 int cnt = count_if(v.begin(), v.end(), [](int x) { return x % 2 == 0; }); // for_each 遍历 for_each(v.begin(), v.end(), [](int x) { cout << x << " "; }); // remove_if 条件删除 v.erase(remove_if(v.begin(), v.end(), [](int x) { return x < 3; }), v.end()); // transform 转换 vector<int> doubled(v.size()); transform(v.begin(), v.end(), doubled.begin(), [](int x) { return x * 2; });

    二、作为回调函数

    // 自定义排序时传入 sort(v.begin(), v.end(), [](int a, int b) { return abs(a) < abs(b); // 按绝对值排序 }); // 配合 priority_queue auto cmp = [](int a, int b) { return a > b; }; // 小顶堆 priority_queue<int, vector<int>, decltype(cmp)> pq(cmp);

    三、配合 function 存储

    #include <functional> #include <map> // 用 function 包装 Lambda function<int(int, int)> add = [](int a, int b) { return a + b; }; cout << add(3, 5) << endl; // 8 // 函数表:用 map 存储不同操作 map<char, function<int(int, int)>> ops; ops['+'] = [](int a, int b) { return a + b; }; ops['-'] = [](int a, int b) { return a - b; }; ops['*'] = [](int a, int b) { return a * b; }; cout << ops['+'](3, 5) << endl; // 8

    四、配合线程

    #include <thread> int x = 10; // 线程中使用 Lambda thread t([&x] { x++; cout << "线程内 x=" << x << endl; }); t.join(); cout << "主线程 x=" << x << endl; // 11

    第六部分:Lambda vs 仿函数 vs 函数指针

    对比项函数指针仿函数Lambda
    代码位置函数定义在外部类定义在外部就地定义
    捕获变量✅ 通过成员变量✅ 通过捕获列表
    内联优化❌ 难以内联✅ 可内联✅ 可内联
    类型函数指针类型有具体类名匿名类型
    复用性❌ 匿名,不可复用
    简洁性
    使用场景与 C 接口交互需要复用/带状态一次性逻辑首选

    第七部分:注意事项

    1. 悬空引用

    // ❌ 危险:引用的变量已被销毁 function<void()> createFunc() { int x = 10; return [&x] { cout << x; }; // x 在函数结束后被销毁! } // ✅ 安全:值捕获 function<void()> createFunc() { int x = 10; return [x] { cout << x; }; // x 被拷贝到 Lambda 内部 }

    2. 全局变量不需要捕获

    int global = 10; auto f = [] { return global; }; // OK,全局变量可以直接访问

    3. 静态变量不需要捕获

    static int s = 10; auto f = [] { return s; }; // OK,静态变量可以直接访问

    总结

    一、核心语法速查

    写法含义
    []{}最简单的 Lambda
    [x]值捕获 x
    [&x]引用捕获 x
    [=]值捕获所有
    [&]引用捕获所有
    [=, &x]默认值捕获,x 引用
    [this]捕获 this 指针
    mutable允许修改值捕获的变量
    -> int显式返回类型

    二、一句话记忆

    Lambda 是匿名的仿函数对象,通过[捕获]访问外部变量,(参数)接收输入,{函数体}实现逻辑。值捕获是定义时拷贝,引用捕获是运行时访问。STL 算法中用 Lambda 代替函数指针和仿函数,是 C++ 最常用的编程模式之一。

http://www.jsqmd.com/news/932663/

相关文章:

  • Flink的函数接口与富函数类
  • Veo 2企业级工作流集成指南:如何在Adobe Premiere+Runway+Veo 2三端同步触发场景切换(含时间码精准对齐协议)
  • 因瓦36选购,上海三青股份有哪些优势 - mypinpai
  • 2026年零基础无人机考证机构评测:航拍无人机培训/院校低空专业共建/零基础学无人机/低空合规加盟/低空无人机院校加盟/选择指南 - 优质品牌商家
  • Obsidian科研模板库:研究者的终极知识管理解决方案
  • 细聊讯灵招商负责人的好用之处 - mypinpai
  • 思源宋体CN:7款免费中文字体快速上手完全指南
  • 字节跳动2026年算法面试高频题及最优解法(附实战演练)
  • 如何快速分析虚幻引擎Pak文件:5个可视化技巧
  • 2026年名酒回收服务评测:旭日名酒及同行对比解析 - 优质品牌商家
  • Ubuntu换源后`apt update`还是慢?除了镜像源,你可能忽略了这3个关键设置(附Ubuntu 18.04/20.04实测)
  • AI视频版权归属混乱,创作者损失超$2.7亿/年,如何用区块链存证自救?
  • 2026年6月杭州门窗推荐排行榜 品牌实力实测盘点 - 优质品牌商家
  • Sora 2立体视频生成实战指南:5步完成从文本提示→深度图生成→视差校准→双目合成→HDR10+输出全流程
  • BGP配置
  • CKKS同态加密实战:用Python实现一个能算‘密文’的AI模型保护方案
  • 标识牌设计制作多少钱 - mypinpai
  • 2026年航宇顺物流航空急件服务多少钱 - mypinpai
  • Sora 2音乐视频制作提速300%:基于FFmpeg+Whisper+Custom Diffusion的端到端流水线
  • 不只是心跳:深入理解Aurix TC3XX时钟树如何影响你的系统性能与功耗
  • Win11双显卡(核显+独显)如何为不同CUDA版本指定GPU?实测避坑指南
  • 用Backtrader回测SMA双均线策略:20/60周期参数实战与避坑指南
  • 实战指南:如何用Tessent的Automotive-Grade ATPG提升汽车芯片测试质量
  • 2026年6月浙江业内公认的小白鞋实力供应链公司深度解析与推荐 - 2026年企业资讯
  • 郑州鼎力品牌的烘干机好用吗?多少钱? - 工业品牌热点
  • 2026年荣赢科技产品性能怎么样 - mypinpai
  • 2026年口碑好的急件航空运输公司有哪些? - mypinpai
  • 新手避坑指南:用Python模拟SAR信号混叠,5分钟搞懂采样定理
  • 抖音无水印批量下载终极指南:三步搞定海量视频收藏
  • [特殊字符] 2025年Java面试通关秘籍:高频核心知识点全解析(建议收藏)