在C++11中,lambda表達式有兩種變量捕獲方式,分別爲值捕獲和引用捕獲。這兩種捕獲的形式如下:
#include <iostream>
int main(int argc, char* argv[])
{
int i = 42;
auto l1 = [i]() //值捕獲
{
std::cout << "l1::i = " << i << std::endl;
};
auto l2 = [&i]() //引用捕獲
{
std::cout << "l2::i = " << i << std::endl;
};
i = 1024;
l1(); //42
l2(); //1024
return 0;
}
//g++ lambda_lifecycle.cpp -o test -std=c++11Copy
使用值傳遞時,編譯器將l1
中的i
初始化爲main
函數中的i
相同的值(42),之後,l1
中的i
與main
函數中的i
不再有任何關係。使用引用傳遞時則不同,l2
中的i
爲main
函數中i
的副本,兩者在內存中的地址是相同的。
所以,在main
函數中更改i
的值時,對l1
無任何影響,而對l2
有影響。l1
中的i
的聲明週期與main
函數中的i
沒有任何關係,l2
中的i
的聲明週期與main
函數中的i
是相同的。這也導致了一個問題:當lambda
表達式的生命週期大於main
函數i
的生命週期時,程序會產生致命錯誤。
#include <iostream>
#include <thread>
#include <chrono>
std::thread t;
void func()
{
int i = 42;
std::cout << "address of i:" << &i << " value of i:" << i << std::endl;
t = std::thread([&i](){
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "address of i:" << &i << " value of i:" << i << std::endl;
});
}
int main(int argc, char* argv[])
{
func();
std::this_thread::sleep_for(std::chrono::seconds(1));
t.join();
return 0;
}Copy
執行結果如下:
g++ lambda_lifecycle.cpp -o test -std=c++11 -lpthread
./test
address of i:0x7fff7ab11ebc value of i:42
address of i:0x7fff7ab11ebc value of i:0Copy
當func
函數執行完成之後,變量i
所在地址被彈出棧,等待2
秒之後,線程t
對變量i
執行讀取操作是未定義行爲。
在使用lambda
表達式捕獲變量時,永遠不要在捕獲局部變量時使用引用捕獲。