Lambda 与闭包
Lambda 是 C++11 引入的匿名函数,让函数式编程风格在 C++ 中变得自然流畅。
基本语法
cpp
// [捕获列表](参数列表) -> 返回类型 { 函数体 }
auto add = [](int a, int b) -> int { return a + b; };
auto add2 = [](int a, int b) { return a + b; }; // 返回类型自动推导
std::cout << add(3, 4) << "\n"; // 7
std::cout << add2(3, 4) << "\n"; // 7
// 立即调用
int result = [](int x) { return x * x; }(5); // 25
// 无参数
auto greet = [] { std::cout << "Hello!\n"; };
greet();捕获方式
cpp
int x = 10, y = 20;
// 值捕获(拷贝)
auto f1 = [x]() { return x; }; // 捕获 x 的副本
x = 99;
std::cout << f1() << "\n"; // 10,不受 x 修改影响
// 引用捕获
auto f2 = [&x]() { return x; };
x = 99;
std::cout << f2() << "\n"; // 99,反映最新值
// 捕获所有(值)
auto f3 = [=]() { return x + y; };
// 捕获所有(引用)
auto f4 = [&]() { x = 100; y = 200; }; // 可修改外部变量
// 混合捕获
auto f5 = [=, &y]() { return x + y; }; // x 值捕获,y 引用捕获
auto f6 = [&, x]() { y = x; }; // y 引用捕获,x 值捕获
// 捕获 this
class Foo {
int val_ = 42;
public:
auto get_lambda() {
return [this]() { return val_; }; // 捕获 this 指针
// C++17:[*this]() { return val_; }; // 捕获 this 的副本(更安全)
}
};mutable Lambda
cpp
// 值捕获默认是 const,用 mutable 允许修改
int count = 0;
auto counter = [count]() mutable {
return ++count; // 修改的是副本,不影响外部 count
};
std::cout << counter() << "\n"; // 1
std::cout << counter() << "\n"; // 2
std::cout << count << "\n"; // 0,外部未变
// 有状态的 lambda(闭包)
auto make_counter = [](int start = 0) {
return [n = start]() mutable { return n++; };
};
auto c1 = make_counter(10);
std::cout << c1() << "\n"; // 10
std::cout << c1() << "\n"; // 11Lambda 的底层实现
cpp
// 编译器将 lambda 转换为匿名类(闭包类型)
int x = 10;
auto f = [x](int y) { return x + y; };
// 等价于:
struct __lambda_1 {
int x; // 捕获的变量成为成员
__lambda_1(int x) : x(x) {}
int operator()(int y) const { return x + y; }
};
__lambda_1 f(x);
// 每个 lambda 都有唯一类型!
auto f1 = [](int x) { return x; };
auto f2 = [](int x) { return x; };
// decltype(f1) != decltype(f2),即使代码完全相同
// 存储 lambda:用 std::function 或模板
std::function<int(int)> func = f1; // 有类型擦除开销
auto func2 = f1; // 保留具体类型,零开销泛型 Lambda(C++14)
cpp
// auto 参数 → 模板 operator()
auto print = [](auto x) { std::cout << x << "\n"; };
print(42);
print("hello");
print(3.14);
// 多个 auto
auto add = [](auto a, auto b) { return a + b; };
add(1, 2); // int
add(1.0, 2.0); // double
add(1, 2.0); // double
// C++20:模板 lambda
auto typed = []<typename T>(T x, T y) { return x + y; };
typed(1, 2); // T = int
// typed(1, 2.0); // 编译错误!T 不能同时是 int 和 doubleLambda 与 STL 算法
cpp
#include <algorithm>
#include <vector>
#include <numeric>
std::vector<int> v = {5, 3, 1, 4, 2};
// 排序
std::sort(v.begin(), v.end(), [](int a, int b) { return a < b; });
// 查找
auto it = std::find_if(v.begin(), v.end(), [](int x) { return x > 3; });
// 变换
std::vector<int> doubled(v.size());
std::transform(v.begin(), v.end(), doubled.begin(), [](int x) { return x * 2; });
// 过滤(C++20 ranges 更优雅)
std::vector<int> evens;
std::copy_if(v.begin(), v.end(), std::back_inserter(evens),
[](int x) { return x % 2 == 0; });
// 累积
int sum = std::accumulate(v.begin(), v.end(), 0,
[](int acc, int x) { return acc + x; });
// C++20 ranges
#include <ranges>
auto result = v | std::views::filter([](int x) { return x % 2 == 0; })
| std::views::transform([](int x) { return x * 2; });递归 Lambda
cpp
#include <functional>
// 方法 1:std::function(有开销)
std::function<int(int)> fib = [&fib](int n) -> int {
return n <= 1 ? n : fib(n-1) + fib(n-2);
};
// 方法 2:Y 组合子(C++14,零开销)
auto fib2 = [](auto self, int n) -> int {
return n <= 1 ? n : self(self, n-1) + self(self, n-2);
};
std::cout << fib2(fib2, 10) << "\n"; // 55
// 方法 3:C++23 deducing this(最优雅)
auto fib3 = [](this auto self, int n) -> int {
return n <= 1 ? n : self(n-1) + self(n-2);
};实用模式
cpp
// 延迟执行
auto deferred = [&]() { expensive_operation(); };
// 稍后执行
deferred();
// 回调注册
class Button {
std::function<void()> on_click_;
public:
void set_click(std::function<void()> cb) { on_click_ = std::move(cb); }
void click() { if (on_click_) on_click_(); }
};
Button btn;
btn.set_click([] { std::cout << "clicked!\n"; });
// 线程任务
#include <thread>
int data = 42;
std::thread t([data]() { // 值捕获,线程安全
std::cout << data << "\n";
});
t.join();
// 立即执行初始化(IIFE)
const auto config = []() {
Config c;
c.load("config.json");
return c;
}(); // 立即执行,config 是 const关键认知
Lambda 本质是编译器生成的匿名类,捕获的变量成为成员。值捕获是拷贝,引用捕获要注意生命周期。存储 lambda 优先用 auto 或模板,std::function 有类型擦除开销。