總述
?C++11提供了對(duì)匿名函數(shù)的支持,稱為L(zhǎng)ambda函數(shù)(也叫Lambda表達(dá)式). 它是定義和使用匿名函數(shù)對(duì)象的一種簡(jiǎn)便的方式袱巨。匿名函數(shù)是我們需要用到的一個(gè)函數(shù)舰蟆,但是又不想去費(fèi)力命名一個(gè)函數(shù)的場(chǎng)景法牲。我們無(wú)需為每個(gè)值或者每種類型)單獨(dú)編寫函數(shù)巾腕,更不必把值保存在讓人厭倦的全局變量中 瘦穆。?利用lambda表達(dá)式可以編寫內(nèi)嵌的匿名函數(shù)抡柿,用以替換獨(dú)立函數(shù)或者函數(shù)對(duì)象辟宗,并且使代碼更可讀爵赵。
????工作的時(shí)候這個(gè)是比較常用的,通過(guò)匿名函數(shù)進(jìn)行函數(shù)內(nèi)部變量的捕獲泊脐,繼而進(jìn)行操作變量等空幻。那么現(xiàn)在就由我來(lái)給大家分享一下,我對(duì)Lambda表達(dá)式的認(rèn)知容客,僅作為一個(gè)基礎(chǔ)的介紹哈秕铛,畢竟C++博大精深约郁,吾輩還需要深究。
作者:良知猶存
轉(zhuǎn)載授權(quán)以及圍觀:歡迎添加微信公眾號(hào):Conscience_Remains
1它的結(jié)構(gòu)
一條lambda表達(dá)式一般會(huì)有以下部分:
1.一個(gè)可能為空的捕獲列表如捅,指明定義環(huán)境中的那些名字能被用在lambda表達(dá)式內(nèi)棍现,以及這些名字的訪問(wèn)形式拷貝還是引用,捕獲列表位于 [] 內(nèi)镜遣。
2.一個(gè)可選的參數(shù)列表己肮,指明lambda表達(dá)式所需的參數(shù),參數(shù)列表位于?() 內(nèi)悲关。
3.一個(gè)可以選的mutable修飾符谎僻,指明該lambda表達(dá)式可能會(huì)修改它自身的狀態(tài)(即,改變通過(guò)值捕獲的變量的副本)
4.一個(gè)可選的 -> 形式的返回類型聲明??
5.一個(gè)表達(dá)式體寓辱,指明要執(zhí)行的代碼艘绍,表達(dá)式位于 {} 內(nèi)。
[捕獲列表](參數(shù)列表) mutable(可選) 異常屬性 -> 返回類型 {// 函數(shù)體}
上面的語(yǔ)法規(guī)則除了[捕獲列表]內(nèi)的東西外秫筏,其他部分都很好理解诱鞠,只是一般函數(shù)的函數(shù)名被略去, 返回值使用了一個(gè)->的形式進(jìn)行这敬。
所謂捕獲列表航夺,其實(shí)可以理解為參數(shù)的一種類型,lambda 表達(dá)式內(nèi)部函數(shù)體在默認(rèn)情況下是不能夠使用函數(shù)體外部的變量的崔涂, 這時(shí)候捕獲列表可以起到傳遞外部數(shù)據(jù)的作用阳掐。
在lambda中,傳參冷蚂、返回結(jié)果以及定義表達(dá)式體和普通的函數(shù)都是一致的缭保,區(qū)別就在于普通函數(shù)沒(méi)有提供局部變量“捕獲”功能,而局部捕獲的功能蝙茶,就意味著lambda可以做局部函數(shù)使用艺骂,而普通函數(shù)不能。
展示一個(gè)小例子證明lambda表達(dá)式的簡(jiǎn)潔性:
Greater than 是一個(gè)函數(shù)對(duì)象尸闸,保存了要比較的值:
structGreater_than {
intval;
Greater_than(lnt v) : val{v} { }? ;
booloperatorO(constpair& r) {returnr.second>val};
};
我們也可以使用?lambda?表達(dá)式?:
auto p =find_if(m.beginO, m.endO,
[](constpair& r) {returnr.second>42; });
每當(dāng)你定義一個(gè)lambda表達(dá)式后彻亲,編譯器會(huì)自動(dòng)生成一個(gè)匿名類(這個(gè)類當(dāng)然重載了()運(yùn)算符),我們稱為閉包類型(closure type)吮廉。
2基本的參數(shù)分析
C++11中的Lambda表達(dá)式捕獲外部變量主要有以下形式:
[]:默認(rèn)不捕獲任何變量苞尝;
[=]:默認(rèn)以值捕獲所有變量;
[&]:默認(rèn)以引用捕獲所有變量宦芦;
[x]:僅以值捕獲x宙址,其它變量不捕獲;
[&x]:僅以引用捕獲x调卑,其它變量不捕獲抡砂;
[=, &x]:默認(rèn)以值捕獲所有變量大咱,但是x是例外,通過(guò)引用捕獲注益;
[&, x]:默認(rèn)以引用捕獲所有變量碴巾,但是x是例外,通過(guò)值捕獲丑搔;
[this]:通過(guò)引用捕獲當(dāng)前對(duì)象(其實(shí)是復(fù)制指針)厦瓢;
[*this]:通過(guò)傳值方式捕獲當(dāng)前對(duì)象;
在上面的捕獲方式中啤月,注意最好不要使用[=]和[&]默認(rèn)捕獲所有變量煮仇。首先說(shuō)默認(rèn)引用捕獲所有變量,你有很大可能會(huì)出現(xiàn)懸掛引用(Dangling references)谎仲,因?yàn)橐貌东@不會(huì)延長(zhǎng)引用的變量的聲明周期浙垫,例如一個(gè)形參傳進(jìn)來(lái)我們進(jìn)行捕獲并作為一個(gè)返回值執(zhí)行。因?yàn)楹瘮?shù)傳參進(jìn)來(lái)之后郑诺,本函數(shù)不會(huì)保存該變量夹姥,函數(shù)執(zhí)行完就會(huì)自動(dòng)釋放,那么這個(gè)時(shí)候返回值就可能產(chǎn)生一個(gè)沒(méi)有意義的結(jié)果辙诞。???????
auto evt_set_status_x = [&](EventType x)
{?
status[x] =true;/*通過(guò)引用捕獲的變量 我們可以進(jìn)行修改變量的數(shù)據(jù)*/
};
[&]是一個(gè)捕獲列表(? capture l ist ),?它指明所用的局部名字(如 x) 將通過(guò)引用訪問(wèn) 佃声。如果我們希望只"捕獲 "x ,則可以寫成 [&x] ;如果希望給生成的函數(shù)對(duì)象傳遞一個(gè)? 的拷貝倘要, 則寫成[ x] 。什么也不捕獲是[]十拣,捕獲所有通過(guò)引用訪問(wèn)的局部名字是[&]封拧,捕獲所有以值訪問(wèn)的局部名字是[=]? 。
????并且lambda表達(dá)式也可以賦值給相對(duì)應(yīng)的函數(shù)指針夭问,這也使得你完全可以把lambda表達(dá)式看成對(duì)應(yīng)函數(shù)類型的指針泽西。
當(dāng)我們需要訪問(wèn)它的局部變量的時(shí)候,我們需要特別定義捕獲列表中的類型
下面是一個(gè)沒(méi)有使用局部變量的lambda表達(dá)式缰趋,所以它的[]里面為空
???????
void part(vector& v){
sort(v.begin,v.end);//排列值
sort(v.begin,v.end,
[](intx,inty){returnabs(x) < abs(y);});//排列絕對(duì)值
}
下面是一個(gè)使用局部變量的lambda表達(dá)式捧杉,所以它的[]里面為空就會(huì)出錯(cuò)???????
voidpart(vector& v){
boolvalue =true;
? ? sort(v.begin,v.end,? ? ? ? ?
? [](intx,inty){returnvalue ? x
}
這時(shí)候就錯(cuò)誤了,因?yàn)槲覀冇玫搅藇alue這個(gè)局部變量秘血,而沒(méi)有進(jìn)行捕獲列表的設(shè)置味抖。
3捕獲使用分析
使用 lambda 雖然簡(jiǎn)單便捷,但也有可能顯得晦澀難懂 灰粮。??
值捕獲
與參數(shù)傳值類似仔涩,值捕獲的前提是變量可以拷貝,不同之處則在于粘舟,被捕獲的變量在 lambda 表達(dá)式被創(chuàng)建時(shí)拷貝熔脂, 而非調(diào)用時(shí)才拷貝:
???????
void value_capture() {
? ? int value =1;
? ? auto copy_value = [value] {
? ? ? ? return value;
? ? };
? ? value =100;
? ? auto stored_value = copy_value();
????std::cout?<<"stored_value?=?"<<?stored_value?<<?std::endl;
}
int main(int argc,char**? argv)
{
? value_capture();
}//?這時(shí),?stored_value?==?1,?而?value?==?100.//?因?yàn)?copy_value?在創(chuàng)建時(shí)就保存了一份?value?的拷貝
?
記得編譯的時(shí)候加 -std=c++11
引用捕獲
與引用傳參類似佩研,引用捕獲保存的是引用,值會(huì)發(fā)生變化:
void reference_capture() {
? ? intvalue =1;
? ? auto copy_value = [&value] {
? ? ? ? return value;
? ? };
? ? value =100;
? ? auto stored_value = copy_value();
? ? std::cout <<"stored_value = "<< stored_value << std::endl;
? ? // 這時(shí), stored_value == 100, value == 100.
? ? // 因?yàn)?copy_value 保存的是引用}
?
泛型lambda表達(dá)式
從C++14開始霞揉,lambda表達(dá)式支持泛型:其參數(shù)可以使用自動(dòng)推斷類型的功能旬薯,而不需要顯示地聲明具體類型。這就如同函數(shù)模板一樣适秩,參數(shù)要使用類型自動(dòng)推斷功能绊序,只需要將其類型指定為auto,類型推斷規(guī)則與函數(shù)模板一樣隶症。就用我最早給出的那個(gè)例子好了政模。
auto evt_set_status_x = [&](EventType x){
status[x] = true;
};
???
?這就是我分享的c++中的lambda表達(dá)式,以后有機(jī)會(huì)再往深入去分析一哈蚂会,其次如果大家有什么更好的思路淋样,歡迎分享交流哈。