C++Lambda表達式

函數(shù)對象是C++中以參數(shù)形式傳遞函數(shù)的一個很好的方法庐椒,我們將函數(shù)包裝成類抡四,并且利用()運算符重載實現(xiàn)宠漩。
typedef class function
{
public:
    void operator()(double x) 
    {
        cout << x << endl;
    }
} FUNCTION;

function是一個類,我們可以實例化一個對象function func;懊直,然后通過func(3.14)的方式來調(diào)用這個類的成員函數(shù)扒吁,如果某個函數(shù)需要這個函數(shù)作為回調(diào)函數(shù),則可以將這個function類的對象傳入即可

因為這是一個類的定義室囊,因此我們完全可以在其中定義一些包含額外信息的成員和一些構(gòu)造函數(shù)雕崩,讓這個函數(shù)對象可以做更多不同的可定制的任務(wù),最終的行為實際上只是調(diào)用了這個()運算符重載函數(shù)融撞。這種做法比C++函數(shù)指針要容易理解得多盼铁,也不容易寫錯。

C++對于變量聲明期的控制在新標準中完全向前兼容尝偎,也就是局部變量一定在退出代碼塊時被銷毀饶火,而不是觀察其是否被引用鹏控。因此,盡管C++的Lambda表達式中允許引用其代碼上下文中的值肤寝,但是實際上并不能夠保證引用的對象一定沒有被銷毀

C++對于變量聲明期的控制在新標準中完全向前兼容当辐,也就是局部變量一定在退出代碼塊時被銷毀,而不是觀察其是否被引用鲤看。因此缘揪,盡管C++的Lambda表達式中允許引用其代碼上下文中的值,但是實際上并不能夠保證引用的對象一定沒有被銷毀


Lambda表達式的基本語法是:
[外部變量訪問方式說明符] mutable (參數(shù)表) -> 返回值類型
{
   語句塊
}
//mutable參數(shù)根據(jù)需要選擇
[=] (int x, int y) -> bool {return x%10 < y%10; }
image.png

“外部變量訪問方式說明符”可以是=或&义桂,表示{}中用到的找筝、定義在{}外面的變量在{}中是否允許被改變。=表示不允許慷吊,&表示允許袖裕。當然,在{}中也可以不使用定義在外面的變量罢浇÷礁常“-> 返回值類型”可以省略。
=表示值傳遞嚷闭,&表示引用傳遞攒岛,例如,&s就表示s變量采用引用傳遞胞锰,不同的說明項之間用逗號分隔灾锯,可以為空,但是方括號不能夠省略嗅榕。第一項可以是單獨的一個=或者&顺饮,表示,所有上下文變量若無特殊說明一律采用值傳遞/引用傳遞凌那,什么都不寫默認為值傳遞兼雄。

Lambda表達式和TR1標準對應(yīng)的 function<返回類型 (參數(shù)表)>對象 是可以互相類型轉(zhuǎn)換的,這樣帽蝶,我們也可以將Lambda表達式作為參數(shù)進行傳遞赦肋,也可以作為返回值返回。

#include <iostream>
#include <string>
#include <functional> //這是TR1的頭文件励稳,定義了function類模板
using namespace std;

typedef class hello {
public:
    void operator()(double x) {
        cout << x << endl;
    }
} hello; //函數(shù)對象的定義佃乘,也是非常常用的回調(diào)函數(shù)實現(xiàn)方法

void callhello(string s, hello func) {
    cout << s;
    func(3.14);
} //一個普通的函數(shù)

void callhello(string s, const function<void (double x)>& func) {
    cout << s;
    func(3.14);
} //這個函數(shù)會接受一個字符串和一個Lambda表達式作為參數(shù)

void callhello(string s, double d) {
    [=] (double x) {
        cout << s << x << endl;
    }(d);
} //這個函數(shù)體內(nèi)定義了一個Lambda表達式并立即調(diào)用

function<void (double x)> returnLambda(string s) {
    cout << s << endl;
    function<void (double x)> f = ([=/*這里必須使用值傳遞,因為s變量在returnLambda返回后就被銷毀*/] (double x) {
        cout << s << x << endl;
    });
    s = "changed"; //這里對s的修改Lambda表達式是無法感知的驹尼,調(diào)用這句語句前s在Lambda表達式中的值已經(jīng)確定了
    return f;
} //這個函數(shù)接受了一個值傳遞的字符串變量s趣避,我們將Lambda表達式作為返回值返回

function<void (double x)> returnLambda2(string& s) {
    cout << s << endl;
    function<void (double x)> f = ([&s/*這里可以使用引用傳遞,因為s是引用方式傳入的新翎,不隨函數(shù)返回而消亡*/] (double x) {
        cout << s << x << endl;
    });
    s = "changed"; //這里對s的修改Lambda表達式是可以感知的程帕,因為s以引用方式參與到Lambda表達式上下文中
    return f;
} //這個函數(shù)接受了一個引用傳遞的字符串變量s住练,將Lambda表達式作為返回值返回


int main()
{
    hello h;
    callhello("hello:", h); //用函數(shù)對象的方式實現(xiàn)功能
    callhello("hello lambda:", -3.14); //這個函數(shù)體內(nèi)定義了一個Lambda表達式并立即調(diào)用
    int temp = 6;
    callhello("hello lambda2:", [&] (double x) -> void {
        cout << x << endl;
        cout << temp++ << endl;
    }); //這個函數(shù)會接受一個字符串和一個Lambda表達式作為參數(shù)
    cout << temp << endl;

     function<void (double x)> f = returnLambda("lambda string"); //這個函數(shù)接受了一個值傳遞的字符串變量s,我們將Lambda表達式作為返回值返回
    f(3.3);
    string lambdastring2 = "lambda string2"; //這個變量在main函數(shù)返回時才被銷毀

    f = returnLambda2(lambdastring2); //這個函數(shù)接受了一個引用傳遞的字符串變量s骆捧,將Lambda表達式作為返回值返回
    f(6.6);
     
    system("pause");
}


值捕獲的坑:

關(guān)于值捕獲要注意的地方是澎羞,與參數(shù)傳值類似,值捕獲的前提是變量可以拷貝敛苇,不同之處則在于妆绞,被捕獲的變量在 lambda 表達式被創(chuàng)建時拷貝,而非調(diào)用時才拷貝枫攀,例如:

void main()
{
    int x = 10;
    auto val_lam = [=]() {return x + 1; };
    x = 20;
    printf("a is %d\n", val_lam());    //lambda函數(shù)的值為11括饶,而不是21
}

我們看到lambda函數(shù)的值為11,而不是21来涨。因為值捕獲在創(chuàng)建時就傳入了图焰。
值捕獲還有一個需要注意的地方,不容易理解蹦掐,也是其特性技羔,就是外部值的連續(xù)性:

void main()
{
    int x = 10;
    auto val_lam = [=]() mutable {x++; return x + 1; };
 
    printf("a is %d\n", val_lam());     //12 
    printf("a is %d\n", val_lam());     //13
    printf("a is %d\n", val_lam());     //14
    printf("x is %d\n", x);             //此時x還是10
}

看到?jīng)],多次調(diào)用lambda函數(shù)卧抗,發(fā)現(xiàn)其記住了外部變量的值藤滥。幾年前學(xué)習到這個特性的時候,也是很不能理解社裆,后來用多了lua的閉包函數(shù)和upvalue拙绊,我現(xiàn)在可以會心的一笑了。事實上利用這個特性可以實現(xiàn)高階函數(shù)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末泳秀,一起剝皮案震驚了整個濱河市标沪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嗜傅,老刑警劉巖金句,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異吕嘀,居然都是意外死亡违寞,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門币他,熙熙樓的掌柜王于貴愁眉苦臉地迎上來坞靶,“玉大人憔狞,你說我怎么就攤上這事蝴悉。” “怎么了瘾敢?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵拍冠,是天一觀的道長尿这。 經(jīng)常有香客問我,道長庆杜,這世上最難降的妖魔是什么射众? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮晃财,結(jié)果婚禮上叨橱,老公的妹妹穿的比我還像新娘。我一直安慰自己断盛,他們只是感情好罗洗,可當我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著钢猛,像睡著了一般伙菜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上命迈,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天贩绕,我揣著相機與錄音,去河邊找鬼壶愤。 笑死淑倾,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的公你。 我是一名探鬼主播踊淳,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼陕靠!你這毒婦竟也來了迂尝?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤剪芥,失蹤者是張志新(化名)和其女友劉穎垄开,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體税肪,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡溉躲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了益兄。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锻梳。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖净捅,靈堂內(nèi)的尸體忽然破棺而出疑枯,到底是詐尸還是另有隱情,我是刑警寧澤蛔六,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布荆永,位于F島的核電站废亭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏具钥。R本人自食惡果不足惜豆村,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望骂删。 院中可真熱鬧掌动,春花似錦、人聲如沸宁玫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撬统。三九已至适滓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間恋追,已是汗流浹背凭迹。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留苦囱,地道東北人嗅绸。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像撕彤,于是被迫代替她去往敵國和親鱼鸠。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,490評論 2 348

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

  • C++ lambda表達式與函數(shù)對象 lambda表達式是C++11中引入的一項新技術(shù)羹铅,利用lambda表達式可以...
    小白將閱讀 85,173評論 15 118
  • 什么是lambda表達式 lambda表達式是一個可調(diào)用的代碼單元蚀狰,我們可以理解為一個未命名的內(nèi)聯(lián)函數(shù),當定義一個...
    土豆吞噬者閱讀 861評論 0 0
  • 聲明 本文內(nèi)容來自微軟 MVP solenovex 的視頻教程——真會C#? - 第4章 委托职员、事件麻蹋、Lambda...
    JeetChan閱讀 754評論 0 3
  • 從** C#3.0開始,可以使用一種新的方法把實現(xiàn)代碼賦予委托: Lambda表達式**焊切。只要有委托參數(shù)類型的地方...
    天堂邁舞閱讀 9,756評論 0 5
  • 我們可以向一個算法傳遞任何類別的可調(diào)用對象(callable object)扮授。對于一個對象或個表達式,如果可以對其...
    趙者也閱讀 297評論 0 0