Lambda表达式详解

前言

最近在刷 LeetCode 时,被官方题解里的 Lambda 表达式卡住了,查了一些资料彻底搞懂。于是把学习心得整理成一篇系统性文章,希望能帮助到同样在入门现代 C++ 的同学们。

一、Lambda表达式是什么?

用一句话定义就是Lambda 表达式是 C++11 引入的一种"就地定义匿名函数"的语法。

它本质是一个表达式,但其功能和行为完全等同于一个没有名字的函数。

传统函数:必须先在某个地方完整定义,才能调用。

Lambda:可以直接在需要的地方"现场写一个函数",写完立刻就能用。

最直观的比喻:

普通函数 = 提前在厨房做好一道菜再端上桌。

Lambda = 直接在餐桌上现场做一道简单的小菜,吃完就走,不用给它起名字。

二、Lambda的基本语法

cpp 复制代码
[捕获列表](参数列表) -> 返回类型 { 函数体 };

各部分含义:

  • 捕获列表\]:\[ \] ------ 决定能否使用外部变量(核心)

  • -> 返回类型:可选,编译器大多能自动推断
  • { 函数体 }:真正的代码逻辑
  • 结尾的分号 ;:必须加!因为它是一个表达式/赋值语句

最简版本的Lambda表达式(无捕获、无参数):

cpp 复制代码
[] {cout<<"Hello Lambda!"<<endl;};

三、Lambda的使用方式

1.基本用法:赋值给变量后调用
cpp 复制代码
#include<iostream>
using namespace std;

int main()
{
    auto greet = [](){
        cout<<"你好,我是一个Lambda!"<<endl;
    };
    greet();  //调用
    return 0;
}
2.带参数+带返回值
cpp 复制代码
auto add = [](int a, int b) -> int {   // 可以省略 -> int,编译器会推断
    return a + b;
};

cout<<add(10, 20)<<endl;   // 输出 30
3.捕获外部变量(这是Lambda的灵魂)

按值捕获(复制一份):

cpp 复制代码
int base = 100;
auto multiply = [base](int x){  //[base]只捕获base
    return base * x;
};

cout<<multiply(5)<<endl;  //500

按引用捕获(可以修改外部变量):

cpp 复制代码
int counter = 0;
auto increment = [&counter]() {   // [&] 按引用捕获所有外部变量
    ++counter;
};

increment();
increment();
cout <<counter<<endl;   // 输出 2

混合捕获:

cpp 复制代码
int a = 1, b = 2;
auto func = [a, &b](int x) {
    b += x;           // 可以修改 b
    return a + b;
};
4.在STL算法中使用
cpp 复制代码
#include <algorithm>
#include <vector>
using namespace std;

vector<int> v = {3, 1, 4, 1, 5, 9};

 // 降序排序
sort(v.begin(), v.end(), [](int x, int y) {
    return x > y;
});

// for_each 遍历并打印
for_each(v.begin(), v.end(), [](int x) {
    cout << x << " ";
});
// 输出:9 5 4 3 1 1
5.捕获this(类成员函数中使用)
cpp 复制代码
class Calculator {
    int value = 42;
public:
    auto getMultiplier() {
        return [this](int x) {        // 捕获 this
            return value * x;
        };
    }
};

int main() {
    Calculator c;
    auto mul = c.getMultiplier();
    cout << mul(10) << endl;   // 420
}
6.mutable关键字
cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    int x = 0;          // 原变量 x,初始值 0
    auto func =  mutable {  // 按值捕获 x,生成一个拷贝(我们叫它 x_copy)
        x++;            // 修改的是 x_copy,不是外面的原 x
        return x;
    };

    cout << func() << " " << func() << endl; // 输出 1 2
    cout << "原变量 x 的值:" << x << endl;   // 原 x 还是 0!
    return 0;
}

只有按值捕获,且需要修改Lambda内部的拷贝时,才需要加 mutable (若按值捕获,且需要修改Lambda内部的拷贝时,没有加 mutable,编译器会报错)。

其他场景(按引用捕获、只读取不修改、按值捕获不修改)都不需要加。

四、Lambda解决了什么问题?

传统 C++ 的痛点:

代码分散:一个小功能也要单独写一个函数或函数对象,逻辑不集中。

命名污染:要给很多只用一次的小函数起名字。

无法方便地捕获上下文:普通函数无法直接使用外部局部变量。

Lambda 完美解决:

就地定义,就地使用,代码可读性大幅提升。

无需命名,减少全局/命名空间污染。

方便捕获上下文,特别适合 STL 算法、回调函数、线程、事件处理等。

就用排序为例:

cpp 复制代码
//C++98写法
bool cmp(int a, int b) { return a > b; }
std::sort(v.begin(), v.end(), cmp);

//C++11Lambda写法(简洁)
std::sort(v.begin(), v.end(), [](int a, int b){ return a > b; });

简单的介绍一下Lambda表达式的底层原理:

编译器在编译时,会把 Lambda 表达式自动转换成一个匿名的类(称为闭包类型 ),这个类重载了 operator()。

cpp 复制代码
// 写的 Lambda
auto add = [](int a, int b){ return a + b; };

// 编译器实际生成的类似代码(简化版)
class __lambda_12345 {
public:
    int operator()(int a, int b) const {
        return a + b;
    }
};

__lambda_12345 add;   // 创建对象
add(3, 5);            // 实际调用 operator()

捕获的变量会变成这个类的成员变量:

=\] → 值拷贝成员 \[\&\] → 引用成员 这就是为什么 Lambda 既是"表达式",又能"像函数一样被调用"。 ### 五、注意点 1.必须加分号 ; 因为 Lambda 是一个表达式,不是函数定义。忘记加分号是初学者最常见的编译错误。 2.捕获方式选错导致 Bug 按引用捕获 \[\&\] 时,Lambda 生命周期不能超过被捕获变量(否则悬空引用)。 按值捕获 \[=\] 时,外部变量修改后 Lambda 不会同步(因为是拷贝)。 3.不要在类成员函数里直接用 \[\&\] 捕获所有 容易导致 this 悬空。推荐显式写 \[this\] 或 \[\&\] + 注意生命周期。 4.mutable 的使用场景 只有按值捕获的变量才需要 mutable,否则编译报错。 5.返回值类型推断 如果函数体有多条 return 语句,返回类型不同,编译器会报错,必须手动写 -\> 类型。 6.性能 Lambda 本身几乎零开销(编译器会内联),但大量捕获大对象时要注意拷贝成本。 7。C++14 及以上增强 泛型 Lambda:\[\](auto x, auto y){ return x + y; } 捕获初始化:[\[x = std::move(obj)\]](){ ... } Lambda表达式总结就是:就地写函数,\[\] 捕获上下文,mutable 改拷贝,生命周期要注意

相关推荐
汀、人工智能17 小时前
[特殊字符] 第21课:最长有效括号
数据结构·算法·数据库架构·图论·bfs·最长有效括号
Boop_wu17 小时前
[Java 算法] 字符串
linux·运维·服务器·数据结构·算法·leetcode
watson_pillow17 小时前
c++ 协程的初步理解
开发语言·c++
故事和你9118 小时前
洛谷-算法1-2-排序2
开发语言·数据结构·c++·算法·动态规划·图论
Tanecious.20 小时前
蓝桥杯备赛:Day6-B-小紫的劣势博弈 (牛客周赛 Round 85)
c++·蓝桥杯
迈巴赫车主20 小时前
蓝桥杯3500阶乘求和java
java·开发语言·数据结构·职场和发展·蓝桥杯
流云鹤20 小时前
Codeforces Round 1090 (Div. 4)
c++·算法
小菜鸡桃蛋狗20 小时前
C++——string(上)
开发语言·c++
wljy120 小时前
第十三届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组(个人见解,已完结)
c语言·c++·算法·蓝桥杯·stl
高一要励志成为佬20 小时前
【数据结构】算法复杂度
数据结构