Lambda 表达式的捕获列表定义了从定义 Lambda 的封闭作用域捕获哪些变量,以及如何捕获这些变量(通过值或引用)。
捕获列表使得 Lambda 表达式能够访问并操作定义它的作用域之外的变量。
下面是捕获列表的各种用法的详细描述:
1. 值捕获(Copy Capture)
通过值捕获的方式,Lambda表达式会复制捕获列表中指定的变量的当前值,并在Lambda表达式内部使用这个复制的值。这意味着Lambda表达式内部对这些变量的修改不会影响原始变量。
#include <iostream>
int main() {
int x = 10;
auto lambda = [x]() mutable {
x *= 2; // 修改的是复制的值,不影响原始变量x
std::cout << "Inside lambda: " << x << std::endl;
};
lambda();
std::cout << "Outside lambda: " << x << std::endl; // 输出10,x的值未改变
}
输出:
Inside lambda: 20
Outside lambda: 10
注意mutable
关键字的使用,如果没有该关键字,这段代码编译会出错。
main.cpp: In lambda function:
main.cpp:6:11: error: assignment of read-only variable ‘x’
6 | x *= 2; // 修改的是复制的值,不影响原始变量x
| ~~^~~~
在上篇文章中我也提到,默认情况下,通过值捕获的变量在Lambda内部是const
的。如果 Lambda 需要修改它通过值捕获的变量,可以添加mutable
关键字。
2. 引用捕获(Reference Capture)
通过引用捕获的方式,Lambda表达式会捕获指向原始变量的引用。这意味着Lambda表达式内部对这些变量的任何修改都会影响到原始变量。
#include <iostream>
int main() {
int x = 10;
auto lambda = [&x] {
x *= 2; // 修改原始变量x的值
std::cout << "Inside lambda: " << x << std::endl;
};
lambda();
std::cout << "Outside lambda: " << x << std::endl; // 输出20,x的值被改变
}
输出:
Inside lambda: 20
Outside lambda: 20
3. 隐式捕获(Implicit Capture)
Lambda 表达式还支持隐式捕获模式,即自动捕获所需的外部变量,而无需显式列出它们。这可以通过[=]
(捕获所有外部变量通过值)或[&]
(捕获所有外部变量通过引用)来实现。
[=]
:通过值捕获所有外部变量。[&]
:通过引用捕获所有外部变量。
#include <iostream>
int main() {
int x = 10, y = 20;
auto lambda_1 = [=] { std::cout << x + y << std::endl; }; // 隐式通过值捕获x和y
lambda_1(); // 输出30
auto lambda_2 = [&] { x = x * 2; y = y * 2; };
lambda_2();
std::cout << "new value, x = " << x << ", y = " << y << std::endl;
}
输出:
30
new value, x = 20, y = 40
4. 混合捕获(Mixed Capture)
Lambda 表达式的捕获列表还可以混合使用值捕获和引用捕获,甚至包括隐式捕获和显式捕获的组合。
#include <iostream>
int main() {
int x = 10, y = 20;
auto lambda = [&, x] { // 通过引用捕获所有变量,但x通过值捕获
std::cout << "Inside lambda, x: " << x << ", y: " << ++y << std::endl;
};
lambda(); // 输出 x: 10, y: 21
std::cout << "Outside lambda, x: " << x << ", y: " << ++y << std::endl;
}
输出:
Inside lambda, x: 10, y: 21
Outside lambda, x: 10, y: 22
5. 捕获this
指针
通过捕获this
指针,Lambda 表达式可以访问类成员变量和成员函数。
#include <iostream>
struct S {
int member = 42;
void f() {
auto lambda = [this] { std::cout << "Member: " << member << std::endl; };
lambda();
}
};
int main() {
S().f(); // 输出 Member: 42
}
输出:
Member: 42
捕获列表是 Lambda 表达式的强大特性之一,有点绕,在后续的版本中,捕获列表又做了一些调整,综合起来更让人迷糊。但是正确使用捕获列表,Lambda 表达式的这种灵活性又可以帮助编写清晰、高效的现代 C++ 代码。