C++11 引入的 Lambda 表达式,是 C++ 现代化进程中的一块基石。

它的核心目的非常明确:让你在需要函数的地方,直接内联地定义一个匿名函数对象。这彻底改变了以往使用函数指针或仿函数(Functor)时代码分散、冗长的问题,极大地简化了标准库算法(如 std::sort, std::for_each)的使用。

下面我将从语法、核心优势、底层机制及使用限制四个方面为你详细介绍。

1. 基本语法

Lambda 表达式的语法结构非常紧凑,由四个主要部分组成:

[capture list] (parameters) -> return_type { function body }
  • [capture list] (捕获列表)这是 Lambda 的灵魂,决定了函数体如何访问外部作用域的变量。
  • (parameters) (参数列表):和普通函数一样,定义形参。如果没有参数,可以省略 ()
  • -> return_type (返回类型):可选。编译器通常能自动推导返回类型;如果无法推导或想显式指定,则需要使用尾置返回类型语法。
  • {function body} (函数体):包含具体的代码逻辑。

2. 核心优势:为什么要用 Lambda?

📍 代码内联,逻辑紧凑

在 C++98 中,如果你想给 std::sort 传一个自定义比较规则,你需要单独写一个函数或者定义一个仿函数类。这会让代码逻辑支离破碎。

  • C++98 (仿函数)
    struct Compare {
        bool operator()(int a, int b) { return a > b; }
    };
    std::sort(v.begin(), v.end(), Compare()); // 必须定义一个 struct
    
  • C++11 (Lambda)
    // 逻辑直接在调用处,一目了然
    std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; });
    
🤝 灵活的变量捕获

Lambda 可以“捕获”定义它所在作用域的变量,这是普通函数做不到的。

  • 值捕获 [x]:拷贝一份 x 的值进 Lambda,Lambda 内部修改不影响外部。
  • 引用捕获 [&x]:直接引用外部的 x,Lambda 内部修改会改变外部变量。
  • 隐式捕获 [=] / [&]
    • [=]:把用到的所有外部变量都拷贝一份进来。
    • [&]:把用到的所有外部变量都引用进来。
🛡️ 支持 mutable 和异常

Lambda 本质上是一个对象,它支持 mutable 关键字(允许修改值捕获的变量副本),也支持抛出异常,比 C 语言的函数指针强大得多。

3. 底层机制:捕获列表详解

捕获列表是 Lambda 最复杂也最强大的部分,它决定了 Lambda 如何与外部世界交互。

捕获方式语法示例说明适用场景
值捕获[x]拷贝 x 进 Lambda,默认是 const 的。只需要读取外部变量,或修改副本。
引用捕获[&x]引用 x,修改会影响外部。需要修改外部变量,或避免大对象拷贝。
隐式值捕获[=]自动拷贝所有用到的外部变量。方便,但要注意不必要的拷贝开销。
隐式引用捕获[&]自动引用所有用到的外部变量。最常用,性能最好,但要小心悬空引用。
混合捕获[=, &x]大部分值捕获,唯独 x 引用捕获。精细控制。
this 捕获[this]捕获当前对象的指针。在类成员函数中访问成员变量。

关于 mutable 的补充:
默认情况下,值捕获的变量在 Lambda 内部是 const 的。如果你想修改值捕获的副本,必须加上 mutable

int x = 10;
// 报错:x 是 const 的
// auto f1 = [x]() { x++; }; 

// 正确:允许修改副本,但不影响外部 x
auto f2 = [x]() mutable { x++; return x; }; 

4. 代码实战:排序与累加

场景一:作为算法策略

这是 Lambda 最常见的用法,直接定义排序规则。

std::vector<int> nums = {4, 1, 8, 2};

// 降序排序
std::sort(nums.begin(), nums.end(), [](int a, int b) {
    return a > b;
});
场景二:捕获外部状态

Lambda 可以像闭包一样保存状态。

int threshold = 5;
std::vector<int> data = {1, 6, 3, 9, 2};

// 查找第一个大于 threshold 的数
auto it = std::find_if(data.begin(), data.end(), [threshold](int val) {
    return val > threshold; // 值捕获 threshold
});

5. 总结与注意事项

特性C++98 (函数指针/仿函数)C++11 (Lambda)
代码位置分散在类外或单独的 struct 中内联,就在调用点
访问上下文难以访问局部变量(需传参或全局)轻松捕获 ([=], [&])
性能函数指针有调用开销零开销(编译器通常将其内联展开)

避坑指南:

  1. 生命周期陷阱:如果你使用 [&](引用捕获),千万不要把 Lambda 对象保存到外部作用域(比如 return 一个 Lambda)。因为 Lambda 销毁后,引用的局部变量就失效了,导致悬空引用。
  2. 全局 Lambda:Lambda 只能捕获栈上的局部变量。全局变量和静态变量不需要捕获,直接在 Lambda 内部使用即可。
  3. 类型:Lambda 的类型是唯一的、匿名的(编译器生成的闭包类型)。如果你想存储 Lambda,通常需要使用 auto 或者 std::function

一句话总结:
Lambda 表达式让 C++ 拥有了函数式编程的能力,它让代码更短、更直观、更高效。在涉及回调、算法策略或线程任务时,首选 Lambda

更多推荐