lambda 表達式 剖析 大前提:捕獲列表裡變數的確定時機。 捕獲列表和參數列表有區別,捕獲列表裡的變數,是在捕獲的時間點就確定了,而不是在lambda調用時確定,參數列表是在調用時才確定。所以當捕獲了一個int i,i=12,然後在lambda後面的代碼又改變i為22,但是當調用lambda的時 ...
lambda 表達式 剖析
大前提:捕獲列表裡變數的確定時機。
捕獲列表和參數列表有區別,捕獲列表裡的變數,是在捕獲的時間點就確定了,而不是在lambda調用時確定,參數列表是在調用時才確定。所以當捕獲了一個int i,i=12,然後在lambda後面的代碼又改變i為22,但是當調用lambda的時候,i值還是12。
剖析點:
1,值捕獲,即使在lambda後面改變了該值,在調用lambda時,這個值還是捕獲時的值。
2,引用捕獲,在lambda後面改變了該值,在調用lambda時,這個值不是捕獲時的值,而是改變後的值。
3,隱式捕獲:
- [=]代表全部採用值捕獲
- [&]代表全部採用引用捕獲
- [=, &val]代表val為引用捕獲,其餘為值捕獲
- [&,val]代表val為值捕獲,其餘為引用捕獲
4,可變lambda,當想在lambda函數體里,修改一個值捕獲的變數是,需要mutable關鍵字。
5,lambda的返回類型,函數體是單一的return語句的話,可以在聲明lambda時,省略返回值的類型。
由剖析點2:引用捕獲,會引發很多血案。比如,被捕獲的引用或者指針指向的對象已經不存在了,然後調用lambda時,就會出現致命錯誤。
警告:當以引用或者指針方式捕獲一個變數時,必須保證在lambda執行時變數是存在的。
建議:
1,捕獲一個普通變數時,如int, string或其他非指針類型,通常可以採用簡單的值捕獲方式。所以,只需關註變數在捕獲時,值是否是所需的值就行。
2,如果捕獲一個指針或迭代器,或引用,就必須保證在lambda被執行的時候,綁定到迭代器,指針或引用的對象仍然存在,而且,需要保證對象是預期的值。因為,有可能在捕獲的時候,是預期的值,但是在執行lambda之前有代碼改變了綁定對象的值,在執行lambda時,就變成不是預期的值了。
3,一般來說,儘量減少捕獲的數據量,來避免潛在的捕獲導致的問題。而且,如果可能的話,儘量避免捕獲指針或引用。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(){
//test1 值捕獲
/*
int i = 1;
auto f = [i]{return i;};
i = 10;
int j = f();
cout << j << endl;
*/
//test2 引用捕獲
/*
int i = 1;
auto f = [&i]{return i;};
i = 10;
int j = f();
cout << j << endl;//3
*/
//test3 隱式值捕獲
/*
int i = 1;
int j = 2;
auto f = [=]{return i + j;};
i = 3;
int m = f();
cout << m << endl;
*/
//test4 隱式引用捕獲
/*
int i = 1;
int j = 2;
auto f = [&]{return i + j;};
i = 3;
int m = f();
cout << m << endl;//5
*/
//test5 隱式,顯式混合1
/*
int i = 1;
int j = 2;
//i為值捕獲,j為引用捕獲
auto f = [=,&j]{return i + j;};
i = 3;
int m = f();
cout << m << endl;//3
*/
//test5 隱式,顯式混合2
/*
int i = 1;
int j = 2;
//i為引用捕獲,j為值捕獲
auto f = [&,j]{return i + j;};
i = 3;
int m = f();
cout << m << endl;//5
*/
//test6 可變lambda
/*
int i = 10;
auto f = [i] () mutable{return ++i;};
int j = f();
cout << j << endl;
*/
/*
const int i = 10;
//編譯錯誤,因為i為const
auto f = [i] () mutable{return ++i;};
int j = f();
cout << j << endl;
*/
//test7 lambda的返回類型
vector<int> ivec{-12,2,-22,3,0};
//改變ivec里的值,負數變成整數
//此lambda不寫返回類型沒有問題.
//transform(ivec.begin(),ivec.end(),ivec.begin(),
// [](int i){return i < 0 ? -i : i;});
//此lambda不寫返回類型也沒有問題.
transform(ivec.begin(),ivec.end(),ivec.begin(),
[](int i){if(i < 0) return -i;
else return i;});
for(const auto &s : ivec){
cout << s << " ";
}
cout << endl;
}