• lambda 是 C++ 里随写随用的 “临时函数”,不用单独定义函数名,写在遍历排序、过滤等场景里很轻便,但类型隐蔽,没法直接传参或复用 ╮(╯▽╰)╭
  • 包装器(比如 function)就是 “收纳盒”,能把 lambda “装起来” 赋予明确类型,不管传参还是存起来反复用都能搞定,就像临时扳手放进工具盒,好拿好存不麻烦╰(✧∇✧)╯

简单说,lambda 负责 “快速造工具”,包装器负责 “好好存工具”,两者搭在一起,既能享受 lambda 的便捷,又能解决它的使用局限,写代码时灵活度和实用性直接拉满~ദ്ദി˶>ω<)✧

------------ lambda表达式 ------------

1. 什么是lambda表达式?

lambda表达式:是一种匿名函数,也就是没有名字的函数。

  • 它是一种简洁的函数定义方式,不需要显式命名函数
  • 它可以方便地定义简短的可调用对象,用于各种需要函数对象的场景
  • 可定义在函数内部(普通函数只能定义在全局、命名空间或类中)
  • 语法层面无显式类型,需通过 auto 或模板参数接收其匿名类型

主要特点:

  1. 匿名性:没有函数名
  2. 简洁性:通常用于简单操作,可以在一行内完成
  3. 临时性:常用于一次性使用或作为参数传递给高阶函数
2. lambda表达式的基本语法形式?

基本语法形式:

代码语言:javascript

AI代码解释

[捕获列表](参数列表) -> 返回类型 
{ 
    	函数体 
}


[capture-list] (parameters) -> return-type 
{ 
    	function-body 
}
  • 捕获列表(capture-list):用于指定在 lambda 表达式中可以访问的外部变量,是 lambda 能够访问所在作用域中变量的关键。
    • 必须出现在 lambda 最开头,编译器通过 [] 识别 lambda 开始
    • 它可以为空,表示不捕获任何外部变量,但是[] 不能省略(空捕获列表是语法要求)
  • 参数列表(parameters):和普通函数的参数列表类似,用于指定 lambda 函数的参数。
    • 它可以为空
    • 若无需参数传递,可连同 () 一起省略(如:无参 lambda 可写 [] { ... })
  • 返回值类型(return-type):指定 lambda 函数的返回值类型。
    • 无返回值时(如:void),可直接省略
    • 返回值类型明确时(如:单一 return 语句),编译器可自动推导,也可省略
  • 函数体(function-body):定义 lambda 函数的具体操作和逻辑,也就是函数执行时要做的事情。
    • 除参数外,可直接使用捕获列表中的变量
    • 即使函数体为空(如:[] {}),{} 也不能省略

通过上面的关于lambda的语法的介绍,现在我们可以明白空lambda的形式了:

代码语言:javascript

AI代码解释

// 空lambda(所有可省略部分均省略)
auto emptyLambda = [] {};  // 合法(但无实际逻辑)
2.1:捕获列表

lambda表达式默认仅能访问自身函数体、参数列表里的变量。若需使用外层作用域(如:包含 lambda 的函数作用域)的变量,需通过 捕获列表 显式声明。


捕获列表决定了 lambda 表达式对外部变量的访问方式,常见的捕获方式有以下几种: 显示捕获:在捕获列表中明确写出变量名,用逗号分隔多个变量,同时通过符号区分捕获方式:

  • 值捕获:直接写变量名(如:[x, y])
  • 引用捕获:变量名前加 &(如:[&z])

隐式捕获:无需逐个写变量名,用符号批量指定捕获规则,编译器会自动识别 lambda 内用到的外层变量并捕获:

  • 按值隐式捕获:捕获列表写 [=] ,lambda 内用到的外层变量全部传值捕获(创建副本)
  • 按引用隐式捕获:捕获列表写 [&] ,lambda 内用到的外层变量全部传引用捕获(使用引用)

混合捕获:同时用隐式捕获符号(= 或 &)和显式变量。


值捕获:在 lambda 内部对捕获变量的修改不会影响外部的原始变量。

  • 例如:[a]表示按值捕获变量 a

代码语言:javascript

AI代码解释

#include <iostream>
using namespace std;

int main()
{
    //1.定义一个局部变量,初始值为10
    int num = 10;

    //2.定义一个lambda表达式,通过值捕获方式获取外部变量num
    auto lambda = [num]()mutable
        {
            //2.1:尝试修改捕获的副本(注意:修改的是副本,不是原始变量)
            num = 20; //注意:此操作不会影响外部的num变量

            //2.2:输出捕获副本的值(此时副本已被修改为20)
            cout << num << endl;  // 输出: 20
        };

    /* 注意事项:默认情况下,按值捕获的变量在lambda表达式内部是不可修改的(const)
    *    
    *      1. 当你使用值捕获 [num] 时,lambda 内部得到的是 num 的一个副本,
    *      2. 但这个副本默认是 const 的,不能修改。
    *      3. 因此当你尝试在 lambda 内部修改 num 时,编译器会报错。
    *      4. 要使按值捕获的变量可以在 lambda 内部修改,需要使用 mutable 关键字
    */

    //3.调用lambda表达式,执行内部逻辑
    lambda();

    //4.输出外部num的值(未被lambda内部修改)
    cout << num << endl;  // 输出: 10
    return 0;
}

在这里插入图片描述

在这里插入图片描述


引用捕获:在 lambda 内部对捕获变量的修改会直接影响外部的原始变量。

  • 例如:[&a],表示按引用捕获变量 a

代码语言:javascript

AI代码解释

#include <iostream>

int main()
{
    //1.定义一个局部变量 num
    int num = 10;

    //2.定义一个lambda表达式,使用引用捕获的方式捕获外部变量 num
    auto lambda = [&num]()
        {
            //2.1:由于是引用捕获,这里对 num 的修改会直接反映到外部的 num 变量上
            num = 20;

            //2.2:输出修改后的 num 的值,此时会输出 20
            std::cout << num << std::endl;
        };

    /* 注意事项:
    *      1. [&num] 表示以引用的方式捕获变量 num,
    *      2. 这意味着 lambda 表达式内部对 num 的操作会直接作用于外部定义的这个 num 变量
    *      3. 因为它们共享同一块内存空间
    */

    //3.调用定义好的lambda表达式,执行其内部的代码逻辑
    lambda();

    //4.再次输出 num 的值
    std::cout << num << std::endl; //因为 lambda 表达式中通过引用捕获修改了 num,所以这里输出 20
    return 0; 
}

在这里插入图片描述

在这里插入图片描述


隐式捕获:可以让编译器自动推导捕获列表。 分为:

  • 按值隐式捕获:[=]
  • 按引用隐式捕获:[&]

代码语言:javascript

AI代码解释

#include <iostream>
int main()
{
    //1.定义两个局部变量
    int num1 = 10;
    int num2 = 20;

    //2.lambda1:按值隐式捕获所有用到的外部变量
    auto lambda1 = [=]()
        {
            //2.1:输出捕获的副本值
            std::cout << num1 << " " << num2 << std::endl;  // 输出: 10 20
        };

    /* 注意事项:
    *   [=]:表示以值捕获方式捕获所有外部变量
    *   lambda内部使用的是外部变量的副本,无法修改原始变量
    */

    //3.lambda2:按引用隐式捕获所有用到的外部变量
    auto lambda2 = [&]()
        {
            //3.1:直接修改外部的num1变量
            num1 = 30;  

            //3.2:输出修改后的外部变量值
            std::cout << num1 << " " << num2 << std::endl;  // 输出: 30 20
        };

    /* 注意事项:
    *   [&]:表示以引用捕获方式捕获所有外部变量
    *   lambda内部使用的是外部变量的引用,可以直接修改原始变量
    */

    //4.调用lambda1:输出捕获时的值副本
    lambda1();  // 输出: 10 20

    //5.调用lambda2:修改外部变量并输出
    lambda2();  // 输出: 30 20

    //6.验证外部变量确实被修改
    std::cout << "外部num1: " << num1 << std::endl;  // 输出: 30

    return 0;
}

在这里插入图片描述

在这里插入图片描述


混合捕获:可以同时使用值捕获和引用捕获。

  • 若开头用 [=](隐式传值),后续显式变量必须传引用
    • 如:[=, &x](其他变量隐式传值,x 显式传引用)
  • 若开头用 [&](隐式传引用),后续显式变量必须传值
    • 如:[&, x](其他变量隐式传引用,x 显式传值)

所以:[=, &a] 表示按值捕获除 a 以外的所有用到的外部变量,而 a 按引用捕获。


总结:捕获列表的常见形式

捕获列表写法

含义

示例(外部变量 int a=10; double b=3.14;)

[]

不捕获任何外部变量

lambda 内无法使用 a、b

[a]

传值捕获 a(修改 lambda 内的 a 不影响外部)

lambda 内 a 是 10 的副本

[&b]

传引用捕获 b(修改 lambda 内的 b 会影响外部)

lambda 内 b 是外部 b 的引用

[=]

传值捕获所有用到的外部变量

lambda 内 a 是副本,b 是副本(修改不影响外部)

[&]

传引用捕获所有用到的外部变量

lambda 内 a、b 都是引用(修改会影响外部)

[a, &b]

混合捕获(a 传值,b 传引用)

lambda 内 a 是副本,b 是引用


 

Logo

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

更多推荐