C++: Lambda表达式详解(从入门到深入)

自从 C++11 引入 Lambda 表达式以来,它已经成为现代 C++ 编程的常用工具。无论是算法调用、回调函数,还是线程池和异步任务中,Lambda 都能大大简化代码。本文将系统介绍 Lambda 表达式的语法、捕获列表、参数传递区别,以及一些常见用法与注意事项。

1. Lambda 表达式的语法

Lambda 的完整语法为:

cpp 复制代码
[capture-list](parameters) mutable(optional) exception(optional) -> return_type(optional) {
    // 函数体
}

各部分含义:

  • capture-list:捕获外部作用域中的变量。

  • parameters:形参列表,和普通函数一样。

  • mutable:允许修改按值捕获的副本。

  • exception :异常说明(如 noexcept)。

  • return_type:返回类型,可省略(编译器会自动推断)。

  • 函数体 {}:Lambda 的实际逻辑。

2. 捕获列表([])详解

捕获列表决定 Lambda 如何使用外部作用域的变量。

2.1 空捕获

cpp 复制代码
auto f = [] { return 42; };
std::cout << f() << std::endl; // 输出 42

👉 不捕获任何外部变量。

2.2 按值捕获

cpp 复制代码
int x = 10;
auto f = [x]() { return x + 1; };
  • 捕获时复制一份 x

  • 后续调用使用副本,不影响外部变量。

若想修改捕获的副本,需要加 mutable

cpp 复制代码
auto f = [x]() mutable { return ++x; };

2.3 按引用捕获

cpp 复制代码
int x = 10;
auto f = [&x]() { return ++x; };
f(); // 修改外部 x,x = 11

2.4 捕获所有变量

cpp 复制代码
int a = 1, b = 2;
auto f1 = [=]() { return a + b; };  // 按值捕获所有局部变量
auto f2 = [&]() { return a + b; };  // 按引用捕获所有局部变量

2.5 混合捕获

cpp 复制代码
int a = 1, b = 2;
auto f = [=, &b]() { return a + (b++); };

2.6 捕获 this

在类成员函数中,常常需要访问对象的成员,可以用 [this] 捕获:

cpp 复制代码
class Test {
    int value = 42;
public:
    void run() {
        auto f = [this]() { return value; };
        std::cout << f() << std::endl; // 输出 42
    }
};

👉 捕获 this 指针后,Lambda 内部可以访问成员变量。

C++17 新增了 [=, this] 的写法,允许同时按值捕获局部变量并捕获 this

3. 捕获 vs 参数传递

很多同学会疑惑:通过参数传递,不也能把变量传进 Lambda 吗?为什么还要捕获?

来看对比:

参数传递

cpp 复制代码
auto f = [](int v) { return v + 1; };
std::cout << f(10) << std::endl;  // 输出 11
  • 值由调用者传入。

  • 灵活,每次调用可传不同的值。

捕获

cpp 复制代码
int base = 100;
auto f = [base](int v) { return base + v; };
std::cout << f(5) << std::endl;  // 输出 105
  • 值在定义时绑定。

  • 简洁,不需要每次调用都显式传入

区别总结

4. 其他语法细节

4.1 mutable

按值捕获的变量默认是只读的,若想修改副本需加 mutable

cpp 复制代码
int x = 10;
auto f = [x]() mutable { return ++x; };
std::cout << f() << std::endl;  // 输出 11

4.2 返回类型推断

cpp 复制代码
auto f = [](int x, int y) { return x + y; };
// 返回类型自动推断为 int

如需显式指定:

cpp 复制代码
auto f = [](int x, int y) -> double { return x + y + 0.5; };

4.3 Lambda 在 STL 中的应用

Lambda 最常见的用途就是配合标准库算法:

cpp 复制代码
std::vector<int> v = {1, 2, 3, 4, 5};

// 自定义条件排序
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; });

// 遍历输出
std::for_each(v.begin(), v.end(), [](int x) { std::cout << x << " "; });

5. 捕获的生命周期陷阱 ⚠️

注意:按引用捕获的变量必须保证在 Lambda 使用时仍然有效。

错误示例:

cpp 复制代码
auto f = []() {
    int x = 10;
    return [&x]() { return x; }; // 危险!x 会悬空
}();

这里返回的 Lambda 内部引用了 x,但 x 在函数结束后已被销毁,调用 Lambda 会导致未定义行为。

6.显示捕获与隐式捕获

显式捕获 (Explicit Capture)

显式捕获就是在 []明确列出要捕获的变量,并指定是按值还是按引用。

示例

cpp 复制代码
int a = 10, b = 20;

auto f1 = [a]() { return a; };     // 显式按值捕获 a
auto f2 = [&b]() { return b; };    // 显式按引用捕获 b
auto f3 = [a, &b]() { return a + b; };  // 混合捕获

优点:

  • 精确控制,代码可读性强。

  • 明确知道 Lambda 使用了哪些变量。

隐式捕获 (Implicit Capture)

隐式捕获就是使用 =&一次性捕获所有在 Lambda 内部使用的外部变量

示例

cpp 复制代码
int a = 10, b = 20;

// 按值隐式捕获(只会捕获 Lambda 内部用到的变量)
auto f1 = [=]() { return a + b; };

// 按引用隐式捕获
auto f2 = [&]() { return a + b; };

特点:

  • 代码简洁,省去一个个列出变量的麻烦。

  • 只会捕获实际在 Lambda 内部用到的变量(编译器自动推断)。

缺点:

  • 可读性差,不容易看出 Lambda 实际依赖了哪些变量。

  • 容易引入不必要的捕获,尤其是在大型函数里。

隐式与显式混合捕获

C++ 允许在隐式捕获的基础上,对某些变量单独指定捕获方式

示例

cpp 复制代码
int a = 10, b = 20, c = 30;

// 默认按值捕获,但 b 显式按引用捕获
auto f1 = [=, &b]() { return a + b + c; };

// 默认按引用捕获,但 a 显式按值捕获
auto f2 = [&, a]() { return a + b + c; };

规则:

  • = 表示默认按值捕获,个别变量可用 &var 显式按引用。

  • & 表示默认按引用捕获,个别变量可用 var 显式按值。

  • 不能同时出现 [=, &][&, =],这是语法错误

小结

7. 总结

  • Lambda 捕获列表:决定如何引入外部作用域变量(值 / 引用 / this)。

  • 参数传递:调用时显式传值,更灵活。

  • 捕获与参数结合使用:是 Lambda 强大的原因之一。

  • 注意生命周期:尤其是按引用捕获。

Lambda 让 C++ 代码更简洁、更现代,是函数式编程思想在 C++ 的体现。掌握 Lambda,是写出现代 C++ 的必经之路。

相关推荐
奔跑吧邓邓子2 小时前
【C++实战(54)】C++11新特性实战:解锁原子操作与异步编程的奥秘
c++·实战·c++11新特性·原子操作·异步编程
Porunarufu2 小时前
JAVA·顺序逻辑控制
java·开发语言
Sylvia-girl3 小时前
C语言中经常使用的函数
c语言·开发语言
~无忧花开~3 小时前
JavaScript学习笔记(十五):ES6模板字符串使用指南
开发语言·前端·javascript·vue.js·学习·es6·js
周杰伦fans3 小时前
C# 23种设计模式详解与示例
开发语言·设计模式·c#
Mr_WangAndy3 小时前
C++设计模式_结构型模式_适配器模式Adapter
c++·设计模式·适配器模式·c++设计模式
bkspiderx3 小时前
C++设计模式之结构型模式:代理模式(Proxy)
c++·设计模式·代理模式
CAE虚拟与现实3 小时前
PyQt和PySide中使用Qt Designer
开发语言·qt·pyqt·qt designer·pyside
Paul_09203 小时前
golang面经——channel模块
开发语言