C++笔记 Lambda表达式

Lambda表达式是C++11引入的核心特性之一,本质是一种匿名函数,可以捕获作用域内的变量,无需单独定义函数名,就能实现简洁、灵活的代码编写,尤其适合作为回调函数、算法参数(如STL算法)等场景,大幅提升代码可读性和开发效率。

一、Lambda表达式的核心作用

Lambda的核心价值的是"轻量、匿名、可捕获",解决了传统函数的两个痛点:

  • 无需单独定义函数(尤其是只使用一次的简单逻辑),减少代码冗余;

  • 可以直接捕获当前作用域的变量(局部变量、类成员等),无需通过参数传递,简化逻辑。

最常见的使用场景:配合STL算法(如sort、for_each)、多线程回调(如thread、async)、条件变量的wait条件判断等。

二、Lambda表达式的语法结构(必背)

Lambda的语法格式固定,核心由5部分组成,其中只有"函数体"是必须的,其余部分可根据需求省略:

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

1. 各部分详解(从左到右)

(1)捕获列表 [ ]:最关键部分,控制Lambda能否访问外部变量

捕获列表用于指定"Lambda能使用哪些外部变量",有5种常见写法,必须牢记:

捕获方式 语法 说明
空捕获 [] 不捕获任何外部变量,Lambda内部只能使用自身参数和全局变量
值捕获 [var1, var2] 捕获指定变量(var1、var2),拷贝一份到Lambda内部,默认不可修改(除非加mutable)
引用捕获 [&var1, &var2] 捕获指定变量的引用,Lambda内部操作会直接修改外部变量(注意变量生命周期,避免悬空引用)
值捕获所有变量 [=] 捕获当前作用域所有外部变量,均为值拷贝,默认不可修改
引用捕获所有变量 [&] 捕获当前作用域所有外部变量,均为引用,可修改外部变量

注意:捕获列表不能混合冲突,比如[=, &var]是允许的(所有变量值捕获,唯独var引用捕获),但[&, var]也是允许的(所有变量引用捕获,唯独var值捕获);但[=, &]、[var, =]是错误的。

(2)参数列表 ( ):和普通函数的参数列表一致

用于接收外部传递给Lambda的参数,语法和普通函数完全相同,可省略(当无参数时)。

cpp 复制代码
// 无参数,可省略()
auto func1 = [] { cout << "无参数Lambda" << endl; };

// 有参数,和普通函数一致
auto func2 = [](int a, int b) { return a + b; };
(3)mutable:可选,解除值捕获的"只读限制"

值捕获的变量,默认在Lambda内部是"只读"的,无法修改;加上mutable后,允许修改Lambda内部的拷贝(不会影响外部原变量)。

cpp 复制代码
int x = 10;
// 错误:值捕获x,默认不可修改
// auto func = [x] { x++; };

// 正确:加mutable,可修改内部拷贝
auto func = [x]() mutable { x++; cout << "内部x:" << x << endl; };
func(); // 输出:内部x:11
cout << "外部x:" << x << endl; // 输出:外部x:10(原变量未变)
(4)noexcept:可选,声明Lambda不会抛出异常

和普通函数的noexcept作用一致,用于告诉编译器,Lambda内部不会抛出异常,可提升性能,尤其在多线程、STL算法中常用。

cpp 复制代码
auto func = [](int a) noexcept { return a * 2; };
(5)返回值类型 -> 类型:可选,编译器可自动推导

如果Lambda函数体只有一条return语句,编译器会自动推导返回值类型,可省略"-> 返回值类型";如果函数体有多条语句且有返回值,必须显式指定返回值类型。

cpp 复制代码
// 自动推导返回值(int),可省略-> int
auto add = [](int a, int b) { return a + b; };

// 必须显式指定返回值,否则编译错误
auto func = [](int a) -> double {
    if (a > 0) return 1.0;
    else return 0.5;
};
(6)函数体 { }:Lambda的核心逻辑

和普通函数的函数体一致,可编写任意合法的C++代码,可使用捕获的变量、参数列表中的参数。

三、Lambda表达式的基本使用示例

示例1:基础用法(无捕获、有参数、自动推导返回值)

cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    // 定义Lambda,计算两个数的和
    auto add = [](int a, int b) {
        return a + b;
    };
    
    // 调用Lambda,和调用普通函数一样
    int result = add(3, 5);
    cout << "3 + 5 = " << result << endl; // 输出:3 + 5 = 8
    return 0;
}

示例2:捕获外部变量(值捕获+引用捕获)

cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    int x = 10, y = 20;
    
    // 值捕获x,引用捕获y
    auto func = [x, &y]() {
        // x是值拷贝,不可修改(无mutable);y是引用,可修改
        // x++; // 错误:值捕获默认只读
        y++;
        cout << "内部x:" << x << ", 内部y:" << y << endl;
    };
    
    func(); // 输出:内部x:10, 内部y:21
    cout << "外部x:" << x << ", 外部y:" << y << endl; // 输出:外部x:10, 外部y:21
    return 0;
}

示例3:配合STL算法(最常用场景)

Lambda最常用的场景就是作为STL算法的参数,替代繁琐的函数对象或全局函数。

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    vector<int> vec = {3, 1, 4, 1, 5, 9};
    
    // 1. 用Lambda排序(降序)
    sort(vec.begin(), vec.end(), [](int a, int b) {
        return a > b; // 降序排序规则
    });
    
    // 2. 用Lambda遍历输出
    for_each(vec.begin(), vec.end(), [](int val) {
        cout << val << " ";
    }); // 输出:9 5 4 3 1 1
    
    return 0;
}

示例4:配合多线程(回调函数)

cpp 复制代码
#include <iostream>
#include <thread>
using namespace std;

int main() {
    int num = 0;
    
    // 引用捕获num,在子线程中修改
    thread t([&num]() {
        for (int i = 0; i < 10000; i++) {
            num++;
        }
    });
    
    t.join(); // 等待子线程结束
    cout << "num = " << num << endl; // 输出:num = 10000
    return 0;
}

四、Lambda表达式的注意事项(面试高频)

1. 捕获变量的生命周期问题(重点坑)

引用捕获的变量,Lambda内部持有变量的引用,如果外部变量生命周期结束(比如出作用域销毁),Lambda再访问该引用会导致悬空引用,程序崩溃。

cpp 复制代码
// 错误示例:引用捕获局部变量,变量销毁后Lambda访问悬空引用
auto getFunc() {
    int x = 10;
    return [&x]() { cout << x << endl; }; // x是局部变量,出函数销毁
}

int main() {
    auto func = getFunc();
    func(); // 崩溃:访问悬空引用
    return 0;
}

解决方案:如果Lambda要在外部使用,优先使用值捕获(拷贝一份,不受外部变量生命周期影响)。

2. Lambda的本质是"函数对象"

Lambda在编译时会被编译器自动转换为一个"匿名的函数对象"(也叫仿函数),因此Lambda可以赋值给std::function(C++11的函数包装器),方便存储和传递。

cpp 复制代码
#include <functional>
#include <iostream>
using namespace std;

int main() {
    // Lambda赋值给function
    function<int(int, int)> add = [](int a, int b) { return a + b; };
    cout << add(2, 3) << endl; // 输出:5
    
    return 0;
}

3. 捕获列表不能捕获全局变量、静态变量

全局变量、静态变量属于全局作用域,Lambda内部可以直接访问,无需在捕获列表中声明(捕获列表只用于捕获"局部变量")。

cpp 复制代码
#include <iostream>
using namespace std;

int g_val = 100; // 全局变量

int main() {
    static int s_val = 200; // 静态变量
    
    // 无需捕获,直接访问全局变量、静态变量
    auto func = []() {
        cout << "全局变量:" << g_val << endl;
        cout << "静态变量:" << s_val << endl;
    };
    
    func(); // 正常输出:全局变量:100  静态变量:200
    return 0;
}

4. mutable的作用范围

mutable只允许修改"值捕获"的拷贝,不会影响外部原变量;引用捕获的变量,即使不加mutable,也可以修改(因为引用本身就是直接操作原变量)。

5. Lambda不能递归调用(除非借助std::function)

Lambda是匿名函数,自身无法直接调用自己;如果需要递归,必须先将Lambda赋值给std::function,再在函数体中调用该function。

cpp 复制代码
#include <functional>
#include <iostream>
using namespace std;

int main() {
    // 先声明function,再赋值Lambda
    function<int(int)> factorial;
    factorial = [&factorial](int n) {
        return n == 1 ? 1 : n * factorial(n - 1);
    };
    
    cout << factorial(5) << endl; // 输出:120(5的阶乘)
    return 0;
}

五、Lambda表达式的核心总结(笔记直接抄)

  1. Lambda是C++11匿名函数,核心语法:[捕获列表](参数列表) mutable noexcept -> 返回值类型 {函数体;}

  2. 捕获列表是核心,分5种:[]、[var]、[&var]、[=]、[&],不可混合冲突;

  3. 值捕获默认只读,需修改内部拷贝加mutable;引用捕获需注意变量生命周期;

  4. 返回值可自动推导(单条return),多条return需显式指定;

  5. 最常用场景:STL算法参数、多线程回调、简单逻辑的临时函数;

  6. 本质是函数对象,可赋值给std::function,支持递归(需借助function)。

Lambda的核心优势是"简洁、灵活",能大幅简化代码,尤其在需要临时函数的场景中,替代传统函数或函数对象,提升代码可读性和开发效率,是C++多线程、STL开发的必备技能。

相关推荐
悟渔2 小时前
用于STM32的C++编程的LED对象
c++·stm32·单片机
We་ct2 小时前
LeetCode 201. 数字范围按位与:位运算高效解题指南
开发语言·前端·javascript·算法·leetcode·typescript
程序员榴莲2 小时前
Java(十二)抽象类
java·开发语言
木子欢儿2 小时前
在 Fedora 上配置 Go 语言(Golang)开发环境
开发语言·后端·golang
超级大只老咪2 小时前
线性递推通用模板
java·开发语言·算法
17(无规则自律)2 小时前
DFS:带重复项的全排列,程序运行全流程解析
c++·算法·深度优先
Dream of maid2 小时前
Python基础 6 (面向对象)
开发语言·python
郝学胜-神的一滴2 小时前
「栈与缩点的艺术」二叉树前序序列化合法性判定:从脑筋急转弯到工程实现
java·开发语言·数据结构·c++·python·算法
skywalk81632 小时前
kitto_plus报错:AttributeError: module ‘kotti_plus‘ has no attribute ‘security‘
linux·开发语言·python