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++ 的必经之路。

相关推荐
第二只羽毛6 小时前
C++ 高性能编程要点
大数据·开发语言·c++·算法
老华带你飞6 小时前
旅游|基于Java旅游信息系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot·旅游
崇山峻岭之间6 小时前
C++ Prime Plus 学习笔记027
c++·笔记·学习
赖small强6 小时前
【Linux C/C++开发】Linux C/C++ 堆栈溢出:原理、利用与防护深度指南
linux·c语言·c++·stack·堆栈溢出
爱学习的梵高先生6 小时前
C++:基础知识
开发语言·c++·算法
oioihoii6 小时前
C++对象生命周期与析构顺序深度解析
java·开发语言·c++
IMPYLH6 小时前
Lua 的 tonumber 函数
开发语言·笔记·后端·junit·游戏引擎·lua
xlq223226 小时前
24.map set(下)
数据结构·c++·算法
晚风吹长发7 小时前
初步了解Linux中文件描述符-fd
linux·运维·服务器·c++·开发·文件
It's now7 小时前
BeanRegistrar 的企业级应用场景及最佳实践
java·开发语言·spring