C++ Lambda 捕获分为基础捕获 、混合捕获 、特殊捕获 和C++14/20 新特性。
1. 基础捕获:值 vs 引用
A. 空捕获 []
不访问任何外部变量。
cpp
int x = 10;
auto f = :ml-search[] {
// std::cout << x; // 错误!x 未捕获
return 42;
};
B. 按值捕获 [x]
拷贝外部变量。内部修改不影响外部 ,且默认不可修改(除非加 mutable)。
cpp
int x = 10;
auto f = :ml-search[x] {
// x = 20; // 错误!按值捕获的变量是 const 的
return x;
};
// x 仍然是 10
C. 按值捕获 + mutable [x] mutable
允许修改副本 ,但不影响外部变量。
cpp
int x = 10;
auto f = :ml-search[x] mutable {
x = 20; // 合法,修改的是内部副本
return x;
};
std::cout << f() << std::endl; // 输出 20
std::cout << x << std::endl; // 输出 10 (外部未变)
D. 按引用捕获 [&x]
直接引用外部变量。内部修改直接影响外部。需注意生命周期(悬空引用风险)。
cpp
int x = 10;
auto f = :ml-search[&x] {
x = 20; // 合法,直接修改外部 x
return x;
};
f();
std::cout << x << std::endl; // 输出 20 (外部已变)
2. 默认捕获与混合捕获(最常用)
E. 默认按值捕获 [=]
Lambda 体内用到的所有局部变量都按值拷贝。
cpp
int a = 1, b = 2;
auto f = :ml-search[=] {
return a + b;
};
注意:在 C++11/14 中,[=] 也会隐式捕获 this 指针(如果使用了成员变量)。
F. 默认按引用捕获 [&]
Lambda 体内用到的所有局部变量都按引用绑定。
cpp
int a = 1, b = 2;
auto f = :ml-search[&] {
a++; b++; // 直接修改外部变量
};
G. 混合捕获:默认值 + 特定引用 [=, &x]
场景:大部分变量只需读取(拷贝开销小或需要快照),但某个大对象或需要修改的变量使用引用。
cpp
int a = 1;
std::vector<int> huge_vec(1000000); // 大对象
auto f = :ml-search[=, &huge_vec] {
// a 是拷贝(安全,快照)
// huge_vec 是引用(避免拷贝百万级数据,且可修改)
huge_vec.push_back(a);
};
H. 混合捕获:默认引用 + 特定值 [&, x]
场景:大部分变量需要修改外部状态,但某个变量(如 ID、配置)需要保留创建时的副本,防止外部意外修改。
cpp
int id = 100;
int status = 0;
auto f = :ml-search[&, id] {
status = 1; // 修改外部 status
// id = 200; // 错误!id 是 const 副本
std::cout << "ID at creation: " << id << std::endl;
};
id = 999; // 外部修改 id
f(); // 输出: ID at creation: 100 (内部仍保留旧值)
3. 类成员与 this 指针
I. 捕获 this [this]
在类成员函数中,若要访问成员变量,必须捕获 this。
cpp
class MyClass {
int val = 10;
public:
void doWork() {
// auto f = :ml-search[val] {}; // 错误!C++11/14/17 不能直接捕获成员变量
auto f = :ml-search[this] {
val = 20; // 通过 this->val 访问
};
f();
}
};
风险:如果 MyClass 对象销毁了,但 Lambda 还被持有(例如在线程池中),调用 Lambda 会导致 Crash。
J. C++20 新特性:按值捕获 *this [*this]
解决上述风险。它会拷贝整个对象到 Lambda 内部。
cpp
// C++20
auto f = :ml-search[*this] {
val = 20; // 修改的是 Lambda 内部拷贝的对象,不影响原对象
};
优点:安全,无悬空指针。缺点:如果对象很大,拷贝开销大。
4. C++14 初始化捕获(Init Capture / Generalized Capture)
这是 C++14 引入的强大功能,允许在捕获列表中定义新变量并初始化。它解决了"移动语义"和"重命名"的问题。
K. 移动捕获(Move Capture)
用于捕获不可拷贝的对象(如 std::unique_ptr)。
cpp
auto ptr = std::make_unique<int>(100);
// auto f = :ml-search[ptr] {}; // 错误!unique_ptr 不可拷贝
auto f = :ml-search[p = std::move(ptr)] {
std::cout << *p << std::endl;
};
// ptr 现在为空,所有权已转移给 Lambda 内部的 p
L. 捕获表达式结果
可以捕获任意表达式的结果,甚至给变量起别名。
cpp
int x = 10;
// 捕获 x 的两倍,并存入名为 doubled_x 的新变量
auto f = :ml-search[doubled_x = x * 2] {
return doubled_x;
};
M. 捕获引用(C++14)
虽然 C++11 有 [&x],但 C++14 允许更灵活的引用初始化。
cpp
int x = 10;
auto f = :ml-search[&ref_x = x] {
ref_x = 20;
};