lambda表達(dá)式

根據(jù)算法接受一元謂詞還是二元謂詞宪巨,我們傳遞給算法的謂詞必須嚴(yán)格接受一個(gè)或兩個(gè)參數(shù)。但是议谷,有時(shí)我們希望進(jìn)行的操作需要更多參數(shù)愈腾,超出了算法對(duì)謂詞的限制憋活。這就需要用到lambda表達(dá)式。

我們可以向一個(gè)算法傳遞任何類別的可調(diào)用對(duì)象(callable object)虱黄。對(duì)于一個(gè)對(duì)象或者表達(dá)式悦即,如果可以對(duì)其使用調(diào)用運(yùn)算符,則稱它為可調(diào)用的。即辜梳,如果e是一個(gè)可調(diào)用的表達(dá)式粱甫,則我們可以編寫代碼e(args),其中args是一個(gè)逗號(hào)分隔的一個(gè)或多個(gè)參數(shù)的列表作瞄。

四種可調(diào)用對(duì)象:函數(shù)茶宵,函數(shù)指針,重載了函數(shù)調(diào)用運(yùn)算符的類和lambda表達(dá)式宗挥。
一個(gè)lambda表達(dá)式表示一個(gè)可調(diào)用的代碼單元乌庶。我們可以將其理解為一個(gè)未命名的內(nèi)聯(lián)函數(shù)。與任何函數(shù)類似契耿,一個(gè)lambda具有一個(gè)返回類型瞒大,一個(gè)參數(shù)列表和一個(gè)函數(shù)體。但與函數(shù)不同搪桂,lambda可能定義在函數(shù)內(nèi)部糠赦,一個(gè)lambda表達(dá)式具有如下形式:

[capture list](parameter list)->return type{function body}

其中capture list是一個(gè)lambda所在函數(shù)中定義的局部變量的列表(通常為空);return type和function body與任何普通函數(shù)一樣分別表示返回類型锅棕,參數(shù)列表和函數(shù)體。但是淌山,與普通函數(shù)不同裸燎,lambda必須使用尾置返回來指定返回類型。我們可以忽略參數(shù)列表和返回類型泼疑,但必須永遠(yuǎn)包含捕獲列表和函數(shù)體德绿。

auto f = []{return 42;}
cout << f() << endl;

在lambda中忽略括號(hào)和參數(shù)列表等價(jià)于指定一個(gè)空參數(shù)列表。如果忽略返回類型退渗,lambda根據(jù)函數(shù)體中的代碼推斷出返回類型移稳。如果函數(shù)體只是一個(gè)return語句,則返回類型從返回的表達(dá)式的類型推斷而來会油。如果lambda的函數(shù)體包含任何單一return語句之外的內(nèi)容个粱,且未指定返回類型,則返回void.
與一個(gè)普通函數(shù)調(diào)用類似翻翩,調(diào)用一個(gè)lambda時(shí)給定的實(shí)參被用來初始化lambda的形參都许。通常,實(shí)參和形參的類型必須匹配嫂冻。但與普通函數(shù)不同胶征,lambda不能有默認(rèn)參數(shù)。因此桨仿,一個(gè)lambda調(diào)用的實(shí)參數(shù)目永遠(yuǎn)與形參數(shù)目相等睛低。一旦形參初始化完畢,就可以執(zhí)行函數(shù)體了。下面是一個(gè)與isShorter函數(shù)完成相同功能的lambda:

[](const string &a, const string &b) {return a.size() < b.size(); }
stable_sort(words.begin(), words.end(),
            [](const string &a, const string &b) {return a.size() < b.size();});

一個(gè)lambda只有在其捕獲列表中捕獲一個(gè)它所在函數(shù)中的局部變量钱雷,才能在函數(shù)體中使用該變量骂铁。捕獲列表只用于局部非static變量,lambda可以直接使用局部static變量和在它所在函數(shù)之外聲明的名字急波。

其實(shí)从铲,當(dāng)定義一個(gè)lambda時(shí),編譯器生成一個(gè)與lambda相對(duì)應(yīng)的未命名類的未命名對(duì)象澄暮,其實(shí)這是一個(gè)函數(shù)對(duì)象名段。捕獲列表中獲得的局部變量成為了這個(gè)函數(shù)對(duì)象的數(shù)據(jù)成呀un。默認(rèn)情況下泣懊,從lambda生成的類都包含一個(gè)對(duì)應(yīng)該lambda所捕獲的變量的數(shù)據(jù)成員伸辟,類似任何類的數(shù)據(jù)成員,lambda的數(shù)據(jù)成員也在lambda對(duì)象創(chuàng)建時(shí)被初始化馍刮。

類似參數(shù)傳遞信夫,變量的捕獲方式也可以是值或引用。首先來說值捕獲卡啰,與傳值參數(shù)類似静稻,采用值捕獲的前提是變量可以拷貝。與參數(shù)不同匈辱,被捕獲的變量的值是在lambda創(chuàng)建時(shí)拷貝振湾,而不是調(diào)用時(shí)拷貝。隨后對(duì)其修改不會(huì)影響到lambda內(nèi)對(duì)應(yīng)的值亡脸。接著來說引用捕獲押搪,引用捕獲與引用返回有著相同的問題和限制,如果我們采用引用方式捕獲一個(gè)變量浅碾,就必須確保被引用的對(duì)象在lambda執(zhí)行的時(shí)候是存在的大州。lambda捕獲的是局部變量,這些變量在函數(shù)結(jié)束后就不復(fù)存在了垂谢。如果lambda可能在函數(shù)結(jié)束后執(zhí)行厦画,捕獲的引用指向的局部變量已經(jīng)消失。所以建議是盡量保持lambda的變量捕獲簡(jiǎn)單化滥朱。

除了顯示列出我們希望使用的來自所在函數(shù)的變量之外苛白,還可以讓編譯器根據(jù)lambda體中的代碼來推斷我們要使用哪些變量。為了指示編譯器推斷捕獲列表焚虱,應(yīng)在捕獲列表中寫一個(gè)&或=购裙。&告訴編譯器采用捕獲引用方式,=則表示采用值捕獲方式鹃栽。如果我們希望對(duì)一部分變量采用值捕獲躏率,對(duì)其他變量采用引用捕獲躯畴,可以混合使用隱式捕獲和顯式捕獲:

[&, c]... [=, &os]...

當(dāng)我們混合使用隱式捕獲和顯式捕獲時(shí),捕獲列表中的第一個(gè)元素必須是一個(gè)&或=薇芝。此符號(hào)指定了默認(rèn)捕獲方式為引用或值蓬抄。當(dāng)混合使用隱式捕獲和顯式捕獲時(shí),顯式捕獲的變量必須使用與隱式捕獲不同的方式夯到。
關(guān)于可變lambda,默認(rèn)情況下嚷缭,對(duì)于一個(gè)值被拷貝的變量,lambda不會(huì)改變其值耍贾。如果我們希望能改變一個(gè)被捕獲的變量的值阅爽,就必須在參數(shù)列表后面加上關(guān)鍵字mutable。因此荐开,可變lambda不能省略參數(shù)列表:

void fcn3()
{
    size_t v1 = 42; // local variable
    // f can change the value of the variables it captures.
    auto f = [v1] () mutable {return ++v1;}
    v1 = 0;
    auto j = f(); // j is 43
}

上例中如果不加() mutable就會(huì)出現(xiàn)錯(cuò)誤信息error: increment of read-only variable ‘v1’

一個(gè)引用捕獲變量是否可以修改依賴于此引用指向的是一個(gè)const類型還是一個(gè)非const類型:

void fcn4()
{
    size_t v1 = 42; // local variable
    // v1 is a reference to a nonconst variable
    // we can change that variable through the reference inside f2
    auto f2 = [&v1] () {return ++v1;}
    v1 = 0;
    auto j = f2(); // j is 1
}

關(guān)于lambda表達(dá)式的返回類型付翁,先看一個(gè)例子:

transform(vi.begin(), vi.end(), vi.begin(), [](int i) {return i < 0 ? -i : i; });

只有一個(gè)return語句,返回一個(gè)條件表達(dá)式的結(jié)果晃听。我們無須指定返回類型百侧,因?yàn)榭梢愿鶕?jù)條件運(yùn)算符的類型推斷出來。但是能扒,如果我們將程序改寫為看起來是等價(jià)的if語句佣渴,就會(huì)產(chǎn)生編譯錯(cuò)誤:

//錯(cuò)誤:不能推斷l(xiāng)ambda的返回類型
transform(vi.begin(), vi.end(), vi.begin(), [](int i){ if(i < 0) return -i; else return i;});

編譯器推斷這個(gè)版本的lambda返回類型為void,但它返回了一個(gè)int值初斑。

當(dāng)我們需要為一個(gè)lambda定義返回類型時(shí)观话,必須使用尾置返回類型:

transform(vi.begin(), vi.end(), vi.begin(),
          [](int i) -> int { if(i < 0) return -i; else return i;});
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市越平,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌灵迫,老刑警劉巖秦叛,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異瀑粥,居然都是意外死亡挣跋,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門狞换,熙熙樓的掌柜王于貴愁眉苦臉地迎上來避咆,“玉大人,你說我怎么就攤上這事修噪〔榭猓” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵黄琼,是天一觀的道長(zhǎng)樊销。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么围苫? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任裤园,我火速辦了婚禮,結(jié)果婚禮上剂府,老公的妹妹穿的比我還像新娘拧揽。我一直安慰自己,他們只是感情好腺占,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布淤袜。 她就那樣靜靜地躺著,像睡著了一般湾笛。 火紅的嫁衣襯著肌膚如雪饮怯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天嚎研,我揣著相機(jī)與錄音蓖墅,去河邊找鬼。 笑死临扮,一個(gè)胖子當(dāng)著我的面吹牛论矾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播杆勇,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼贪壳,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了蚜退?” 一聲冷哼從身側(cè)響起闰靴,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎钻注,沒想到半個(gè)月后蚂且,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡幅恋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年杏死,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捆交。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡淑翼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出品追,到底是詐尸還是另有隱情玄括,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布肉瓦,位于F島的核電站惠豺,受9級(jí)特大地震影響银还,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜洁墙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一蛹疯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧热监,春花似錦捺弦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至苦始,卻和暖如春寞钥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背陌选。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工理郑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人咨油。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓您炉,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親役电。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赚爵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • C++ lambda表達(dá)式與函數(shù)對(duì)象 lambda表達(dá)式是C++11中引入的一項(xiàng)新技術(shù),利用lambda表達(dá)式可以...
    小白將閱讀 85,247評(píng)論 15 118
  • 簡(jiǎn)介 概念 Lambda 表達(dá)式可以理解為簡(jiǎn)潔地表示可傳遞的匿名函數(shù)的一種方式:它沒有名稱法瑟,但它有參數(shù)列表冀膝、函數(shù)主...
    劉滌生閱讀 3,202評(píng)論 5 18
  • Lambda表達(dá)式 利用行為參數(shù)化這個(gè)概念,就可以編寫更為靈活且可重復(fù)使用的代碼霎挟。但同時(shí)窝剖,使用匿名類來表示不同的行...
    謝隨安閱讀 871評(píng)論 2 0
  • 聲明:本文翻譯自The Java? Tutorials(官方文檔) 簡(jiǎn)述 匿名類有一個(gè)問題,如果匿名類的實(shí)現(xiàn)非常簡(jiǎn)...
    猴子小皮球閱讀 4,657評(píng)論 0 9
  • ?早上逛朋友圈氓扛,突然看到某友發(fā)了一段挺奇怪的話,隱約覺的不太對(duì)勁论笔,于是點(diǎn)進(jìn)去看她的相冊(cè)采郎,一看大吃一驚,果然出事了狂魔!...
    安麗說閱讀 290評(píng)論 0 1