Lambda 捕獲列表 [=] 捕獲所有可訪問的外部變量,這里說的外部變量是指那些變量?
在 C++中,Lambda 表達式的捕獲列表 [=] 表示以值(拷貝)的方式捕獲所有可訪問的外部變量。這里的"外部變量"指的是 Lambda 表達式所在作用域內可見的局部變量、函數參數、類的成員變量(通過this指針)。
一、[=] 捕獲的變量范圍
1. 局部變量和函數參數
Lambda 所在作用域內的非靜態局部變量和函數參數會被以值的方式捕獲。
void func(int param) {
int local_var = 10;
static int static_var = 20;
auto lambda = [=] {
// 捕獲 param 和 local_var 的值
std::cout << param << ", " << local_var << std::endl;
// static_var 是靜態變量,無需捕獲,直接訪問
std::cout << static_var << std::endl;
};
lambda();
}
捕獲的變量:param(函數參數)、local_var(局部變量)。
不捕獲的變量:static_var(靜態變量,直接訪問)。
2. 類的成員變量
如果 Lambda 定義在類的成員函數中,[=] 會隱式捕獲 this 指針,從而可以訪問類的成員變量。
本質是通過this指針訪問成員變量,而不是直接捕獲成員變量的值!
class MyClass {
int member_var = 42;
public:
void method() {
auto lambda = [=] {
// 實際捕獲的是 this 指針!
std::cout << member_var << std::endl; // 等價于 this->member_var
};
lambda();
}
};
捕獲的變量:this 指針(隱式捕獲)。
直接捕獲成員變量:成員變量不會被單獨拷貝。
3. 塊作用域內的變量
如{}代碼塊內定義的變量:
{
int x = 5;
auto lambda = [=]() { return x; }; // 捕獲x的值
}
二、不屬于外部變量的情況
1. 全局變量和靜態變量
全局變量和靜態變量不屬于"外部變量"的范疇,因為它們不在 Lambda 的局部作用域內,可以直接訪問,無需捕獲。
int global_var = 100;
void func() {
static int static_local = 200;
auto lambda = [=] {
std::cout << global_var << std::endl; // 直接訪問全局變量
std::cout << static_local << std::endl; // 直接訪問靜態局部變量
};
lambda();
}
三、[=] 的注意事項
1. 捕獲的是變量當前的值
Lambda 在定義時拷貝變量的值,捕獲的變量是 Lambda 定義時的 瞬時快照,后續外部變量的修改不會影響 Lambda 內部的值。
int main() {
int x = 5;
auto lambda = [=] { std::cout << x << std::endl; };
x = 10;
lambda(); // 輸出 5,而不是 10
}
但是這里類成員變量卻是不同的,當Lambda通過[=]捕獲this指針時,訪問的成員變量是實時的,而非定義時的快照:
class MyClass {
int data = 42;
public:
void method() {
auto lambda = [=] { std::cout << data; }; // 捕獲this指針
data = 100;
lambda(); // 輸出100,而非42!訪問的是當前data的值
}
};
測試驗證:
2. mutable 關鍵字的作用
若希望修改捕獲的值副本,需添加 mutable,但修改不影響外部變量。
int x = 5;
auto lambda = [=]() mutable {
x = 10; // 修改的是副本
std::cout << x << std::endl; // 輸出 10
};
lambda();
std::cout << x << std::endl; // 輸出 5(外部變量未變)
如果不加 mutable, 編譯報錯:
3. 懸垂 this 指針的風險
如果 Lambda 的生命周期超過對象,訪問成員變量會導致未定義行為。
class MyClass {
int data = 42;
public:
autoget_lambda(){
return [=] { std::cout << data << std::endl; }; // 捕獲 this 指針
}
};
intmain(){
std::function<void()> func;
{
MyClass obj;
func = obj.get_lambda(); // 捕獲 obj 的 this 指針
} // obj 被銷毀,this 指針失效!
func(); // 未定義行為!
}
4. 僅捕獲實際使用的變量
[=] 只會捕獲 Lambda 函數體中實際使用 的外部變量。未使用的變量不會被捕獲。例如:
int a = 1, b = 2;
auto lambda = [=]() { return a; }; // 僅捕獲a,不捕獲b
四、[=] 與 [&] 的對比
[&] 和 [=] 捕獲的范圍一樣,只不過是引用類型的。
捕獲方式 | 捕獲內容 | 行為 | 風險 |
[=] | 局部變量、參數、 | 值拷貝 | 懸垂 |
[&] | 局部變量、參數、 | 引用捕獲 | 懸垂引用 |
五、常用寫法
顯式捕獲關鍵變量避免使用 [=] 或 [&],優先顯式列出需要捕獲的變量,增強可讀性。
auto lambda = [x, &y] { /* 只捕獲 x(值)、y(引用) */ };
若 Lambda 可能比捕獲的變量生命周期更長,應避免值捕獲大對象(可能造成拷貝開銷)或引用捕獲局部變量。
- 優先使用值捕獲的場合
- 需要保存當前狀態快照。
- 避免外部變量被意外修改。
六、總結
[=] 捕獲的"外部變量":Lambda 所在作用域內的局部變量、函數參數、this 指針(用于訪問成員變量)。類成員變量通過this指針訪問,其值是實時的,而非定義時的拷貝。
- 不捕獲的變量:全局變量、靜態變量、類的成員變量(通過 this 間接訪問)。
- 關鍵風險:懸垂 this 指針和引用,需結合智能指針或生命周期管理。