咱先抛开 “表达式”“匿名函数” 这些吓人的词,用个生活例子开头:你在家做饭,突然发现酱油瓶拧不开 —— 你不用专门去楼下找个 “专业拧瓶盖的人”(写个独立函数),而是直接喊旁边看电视的室友:“帮我拧下酱油瓶!”(临时找个 “临时工” 干活)。

Lambda 表达式,就是 C++ 里的 “代码临时工”——不用提前写一个独立函数,需要用的时候,当场写一段小代码块,用完就扔。比如排序、遍历数组时,临时需要一段 “比较逻辑” 或 “处理逻辑”,用 Lambda 比单独写函数方便 10 倍。

一、Lambda 长啥样?—— 拆解 “临时工的工作证”

Lambda 的基本格式很固定,就像给临时工发的工作证,分 4 个部分:

cpp

运行

[捕获规则] (参数列表) -> 返回值类型 { 具体干活的代码 };

咱逐个拆解,用 “拧酱油瓶” 的例子对应:

部分大白话解释对应 “拧酱油瓶” 的例子可省略情况
[捕获规则]要不要 “带外面的东西” 进来干活(比如外面的变量)要不要让室友带 “开瓶器”(外面的工具)可以空着(不带任何东西)
(参数列表)干活需要的 “原材料”(比如要拧的瓶子)给室友 “酱油瓶”(需要处理的对象)没参数时可以空着,甚至省掉 ()
-> 返回值类型干完活要不要 “交成果”(比如拧开后要不要递回来)拧开后要不要把瓶子递给你能自动推导出返回值时可省掉
{ 代码 }具体怎么干活(拧瓶盖的步骤)室友拧瓶盖的动作(左手扶瓶、右手拧)必须有,不然临时工不知道干啥

二、从最简单的开始:不用带东西、不用参数的 Lambda

先写一个 “啥都不带、啥参数没有,就干一件小事” 的 Lambda,比如 “打印一句话”:

cpp

运行

#include <iostream>
using namespace std;

int main() {
    // 这就是一个Lambda:啥都不带、没参数、没返回值,就打印一句话
    auto print_hello = []() { 
        cout << "我是Lambda,我在干活!" << endl; 
    };

    // 调用Lambda(跟调用函数一样,加个())
    print_hello(); 

    return 0;
}

运行结果:我是Lambda,我在干活!

关键理解

  • auto print_hello:用auto自动推导 Lambda 的类型(不用管它具体叫啥,反正能存起来);
  • []:空的,意思是 “不捕获外面任何变量”(临时工不带任何工具);
  • ():空的,意思是 “不需要参数”(临时工不用原材料,直接干活);
  • 没写-> 返回值类型:因为代码里没有return,C++ 能自动推导出 “返回值是 void”(干完活不用交成果)。

三、进阶 1:需要 “原材料”(参数)的 Lambda

比如要一个 “计算两个数相加” 的 Lambda,需要传入两个数(参数):

cpp

运行

int main() {
    // Lambda:需要两个int参数,返回它们的和
    auto add = [](int a, int b) -> int { 
        return a + b; // 有return,返回值是int
    };

    // 调用Lambda,传两个参数
    int result = add(3, 5); 
    cout << "3+5=" << result << endl; // 输出:3+5=8

    return 0;
}

小优化:返回值类型能省!因为 C++ 能从return a + b里看出 “返回值是 int”,所以-> int可以删掉,简化成:

cpp

运行

auto add = [](int a, int b) { return a + b; }; // 一样能用

四、进阶 2:需要 “带外面的东西”(捕获外部变量)

这是 Lambda 最核心的功能 —— 能 “拿外面的变量” 进来用。比如外面有个变量x=10,想让 Lambda 用它来计算:

情况 1:值捕获(拿副本,改了不影响外面)

[x]表示 “值捕获 x”—— 相当于给临时工 “复印一份 x 的资料”,临时工改自己的副本,不影响你手里的原件:

cpp

运行

int main() {
    int x = 10; // 外面的变量

    // Lambda:值捕获x(拿x的副本),参数是y
    auto calculate = [x](int y) { 
        // x = 20; // 编译错误!值捕获的副本不能改(默认是const的)
        return x + y; // 用副本x和参数y计算
    };

    int result = calculate(5); 
    cout << "10+5=" << result << endl; // 输出:10+5=5
    cout << "外面的x还是:" << x << endl; // 输出:外面的x还是:10(没被改)

    return 0;
}

想改值捕获的副本? 加个mutable(意思是 “允许改副本”):

cpp

运行

auto calculate = [x](int y) mutable { 
    x = 20; // 现在能改副本了
    return x + y; 
};
int result = calculate(5); // 20+5=25
cout << "外面的x还是:" << x << endl; // 还是10(改的是副本)

情况 2:引用捕获(拿地址,改了影响外面)

[&x]表示 “引用捕获 x”—— 相当于给临时工 “x 的地址”,临时工改的就是你手里的原件(跟左值引用int&一个意思):

cpp

运行

int main() {
    int x = 10;

    // Lambda:引用捕获x(拿x的地址)
    auto add_to_x = [&x](int y) { 
        x += y; // 改的是外面的x(因为是引用)
    };

    add_to_x(5); 
    cout << "外面的x变成了:" << x << endl; // 输出:外面的x变成了:15

    return 0;
}

注意:引用捕获要小心!如果外面的变量先 “消失”(比如函数结束),Lambda 再用引用就会出错(相当于拿了个无效地址)。

情况 3:批量捕获(不用一个个写变量)

如果外面变量多,不想一个个写,用批量捕获规则:

  • [=]:值捕获 “所有用到的外部变量”(省心,不用列变量名);
  • [&]:引用捕获 “所有用到的外部变量”(更省心,但要注意变量生命周期);

例子([=]批量值捕获):

cpp

运行

int main() {
    int a = 2, b = 3;

    // Lambda:值捕获所有用到的变量(a和b)
    auto multiply = [=]() { 
        return a * b; // 直接用外面的a和b,不用一个个写[ a, b ]
    };

    cout << "2*3=" << multiply() << endl; // 输出:2*3=6

    return 0;
}

五、Lambda 最常用的场景:配合 STL 算法

Lambda 最大的用处,是在sort(排序)、for_each(遍历)这些 STL 算法里,临时写 “处理逻辑”。比如给数组排序,不用单独写一个比较函数:

例子 1:给数组按 “从大到小” 排序

cpp

运行

#include <iostream>
#include <vector>
#include <algorithm> // 包含sort
using namespace std;

int main() {
    vector<int> nums = {3, 1, 4, 1, 5};

    // 用sort排序,第三个参数直接写Lambda(比较逻辑)
    sort(nums.begin(), nums.end(), [](int a, int b) { 
        return a > b; // 比较规则:a比b大就排在前面(从大到小)
    });

    // 遍历数组,打印结果
    for (int num : nums) {
        cout << num << " "; // 输出:5 4 3 1 1
    }

    return 0;
}

如果不用 Lambda,你得单独写一个比较函数:

cpp

运行

// 单独写的比较函数
bool compare(int a, int b) {
    return a > b;
}
// 然后sort里传函数名:sort(nums.begin(), nums.end(), compare);

对比下来,Lambda 不用 “额外写函数”,逻辑直接嵌在sort里,代码更紧凑。

例子 2:遍历数组,给每个元素加 10

for_each遍历,Lambda 负责 “给元素加 10”:

cpp

运行

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    vector<int> nums = {1, 2, 3};

    // for_each:遍历每个元素,用Lambda处理
    for_each(nums.begin(), nums.end(), [](int& num) { 
        num += 10; // 引用捕获元素,直接修改原数组
    });

    for (int num : nums) {
        cout << num << " "; // 输出:11 12 13
    }

    return 0;
}

六、总结:Lambda 的核心优点(为啥要用它?)

  1. 简洁:不用单独写函数,逻辑当场写、当场用,代码少;
  2. 灵活:能捕获外部变量,配合 STL 算法超方便;
  3. 临时:用完就扔,不用给函数起名字(很多时候函数只用一次,起名字反而麻烦)。

七、给 “菜鸡” 的避坑指南

  1. 值捕获默认不能改:要改副本必须加mutable
  2. 引用捕获要注意生命周期:外面的变量没了,Lambda 就别用引用了;
  3. 别滥用捕获:能不捕获就不捕获,能值捕获就别引用捕获(减少 bug);
  4. 返回值能省就省:C++ 能自动推导返回值,不用硬写-> 类型

如果现在让你写一个 “给数组里的偶数加 5” 的逻辑,你应该能想到:用for_each遍历,配合一个 “判断是否是偶数、是就加 5” 的 Lambda—— 这就是 Lambda 的魅力,简单场景下,比写独立函数快多了!

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐