什么是闭包?

闭包是一个能记住并访问其创建时作用域中变量的函数,即使那个作用域已经结束了。

闭包 = 一个函数 + 它创建时所“记住”的外部变量(被捕获的变量)

// 普通函数:空手的旅行者
void traveler() {
    // 只能使用路上找到的东西
}

// 闭包:背着背包的旅行者
auto traveler_with_bag = [](/*背包里装的东西*/) {
    // 可以随时使用背包里的东西
};

C++ 中闭包的载体:Lambda 表达式

在 C++11 及以后,闭包主要通过 Lambda 表达式实现。

基本语法:

[capture_list](parameter_list) -> return_type {
    // function body
}

[capture_list]:捕获列表 —— 决定哪些外部变量被“打包”进闭包(这是闭包的关键!)
(parameter_list):参数列表(和普通函数一样)
-> return_type:返回类型(可省略,编译器自动推导)
{}:函数体

捕获列表

捕获方式含义是否构成闭包?
[]不捕获任何变量❌ 不是闭包(只是匿名函数)
[x]值捕获 x(拷贝一份)是闭包
[&x]引用捕获 x是闭包(但需小心生命周期)
[=]值捕获所有用到的外部变量是闭包
[&]引用捕获所有用到的外部变量是闭包
[this]捕获当前对象指针(C++17 起可写 [=, this])是闭包

代码示例

1. 值捕获

#include <iostream>
using namespace std;

int main() {
    int base = 10;
    
    // 闭包:捕获 base 的值
    auto adder = [base](int x) {
        return x + base;  // 使用被捕获的 base
    };

    cout << adder(5) << endl;  // 输出 15

    base = 100;  // 修改原变量
    cout << adder(5) << endl;  // 仍输出 15!因为闭包用的是拷贝

    return 0;
}

2. 引用捕获

#include <iostream>
using namespace std;

int main() {
    int value = 42;
    
    auto getter = [&value]() {  // 引用捕获
        return value;
    };

    value = 100;
    cout << getter() << endl;  // 输出 100(引用会变)

    // 如果 value 被销毁,getter() 就是未定义行为!
    return 0;
}

3. 闭包作为函数返回值(经典用法)

#include <iostream>
#include <functional>  
using namespace std;

// 返回一个闭包
auto make_multiplier(int factor) {
    return [factor](int x) {  // 值捕获 factor
        return x * factor;
    };
}

int main() {
    auto double_it = make_multiplier(2);
    auto triple_it = make_multiplier(3);

    cout << double_it(5) << endl;  // 10
    cout << triple_it(5) << endl;  // 15

    // 即使 make_multiplier 已返回,factor 仍有效!
    return 0;
}

4.闭包与STL算法的融合

#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;

int main() {
    vector<int> numbers = {1, 2, 3, 4, 5, 6};
    int threshold = 3;

    // 用闭包定制查找条件
    auto it = find_if(numbers.begin(), numbers.end(),
        [threshold](int n) { return n > threshold; }
    );

    if (it != numbers.end()) {
        cout << "First number > " << threshold << " is " << *it << endl;
    }

    return 0;
}

闭包对象的本质

例如

int a = 5;
auto f = [a](int x) { return x + a; };

等价于

class __lambda_123 {
    int a_;  // 捕获的变量
public:
    __lambda_123(int a) : a_(a) {}
    int operator()(int x) const { return x + a_; }
};

__lambda_123 f(a);  // f 是一个对象!重载了 operator() 的对象

注意事项

1. 引用捕获的生命周期问题

auto bad() {
    int x = 10;
    return [&x] { return x; };  // 危险!x 在函数返回后销毁, 正确做法:用值捕获 [x],或确保变量活得足够久。
}

2. 修改被捕获的值

默认 Lambda 是 const 的,不能修改捕获的值:

int count = 0;
auto f = [count]() { count++; };  // 编译错误!

解决方案:加 mutable 关键字

auto f = [count]() mutable { count++; };  // 允许修改拷贝,修改的是拷贝,不影响外部 count。

3. [=]和 [&]的陷阱

int a = 1, b = 2;
auto f = [=, &b]() {  // 值捕获所有,但 b 用引用
    a = 10;           // 错误!a 是值捕获,默认 const
    b = 20;           // b 是引用,可修改
};

C++ 闭包速查表

项目说明
什么是闭包?Lambda + 捕获的外部变量
语法形式[capture](params) { body }
关键部分捕获列表 [...]
值捕获 [x]安全,拷贝变量,生命周期由闭包管理
引用捕获 [&x]高效,但需手动保证变量存活
本质编译器生成的匿名类对象(重载 operator())
用途回调、算法定制、延迟计算、状态封装

更多推荐