C 中的Lambda表示式詳解

2021-08-30 19:45:48 字數 3648 閱讀 4736

一直都在提醒自己,我是搞c++的;但是當c++11出來這麼長時間了,我卻沒有跟著隊伍走,發現很對不起自己的身份,也還好,發現自己也有段時間沒有寫c++**了。今天看到了c++中的lambda表示式,雖然用過c#的,但是c++的,一直沒有用,也不知道怎麼用,就可憐的連lambda語法都看不懂。好了,這裡就對c++中的lambda進行乙個簡單的總結,就算是對自己的乙個交代,我是搞c++的,我是乙個c++ programmer。

一段簡單的code

我也不是文藝的人,對於lambda的歷史,以及lambda與c++的那段淵源,我也不是很熟悉,技術人,講究拿**說事。

#includeusing namespace std;

int main()

; return 0;

}

當我第一次看到這段**時,我直接凌亂了,直接看不懂啊。上面這段**,如果你看懂了,下面的內容就當時複習了;如果看不懂了,就接著和我一起總結吧。

基本語法

簡單來說,lambda函式也就是乙個函式,它的語法定義如下:

[capture](parameters) mutable ->return-type

[capture]:捕捉列表。捕捉列表總是出現在lambda函式的開始處。實際上,是lambda引出符。編譯器根據該引出符判斷接下來的**是否是lambda函式。捕捉列表能夠捕捉上下文中的變數以供lambda函式使用;

(parameters):引數列表。與普通函式的引數列表一致。如果不需要引數傳遞,則可以連同括號「()」一起省略;

mutable:mutable修飾符。預設情況下,lambda函式總是乙個const函式,mutable可以取消其常量性。在使用該修飾符時,引數列表不可省略(即使引數為空);

->return-type:返回型別。用追蹤返回型別形式宣告函式的返回型別。我們可以在不需要返回值的時候也可以連同符號」->」一起省略。此外,在返回型別明確的情況下,也可以省略該部分,讓編譯器對返回型別進行推導;

:函式體。內容與普通函式一樣,不過除了可以使用引數之外,還可以使用所有捕獲的變數。

與普通函式最大的區別是,除了可以使用引數以外,lambda函式還可以通過捕獲列表訪問一些上下文中的資料。具體地,捕捉列表描述了上下文中哪些資料可以被lambda使用,以及使用方式(以值傳遞的方式或引用傳遞的方式)。語法上,在「」包括起來的是捕捉列表,捕捉列表由多個捕捉項組成,並以逗號分隔。捕捉列表有以下幾種形式:

[var]表示值傳遞方式捕捉變數var;

[=]表示值傳遞方式捕捉所有父作用域的變數(包括this);

[&var]表示引用傳遞捕捉變數var;

[&]表示引用傳遞方式捕捉所有父作用域的變數(包括this);

[this]表示值傳遞方式捕捉當前的this指標。

上面提到了乙個父作用域,也就是包含lambda函式的語句塊,說通俗點就是包含lambda的「{}」**塊。上面的捕捉列表還可以進行組合,例如:

[=,&a,&b]表示以引用傳遞的方式捕捉變數a和b,以值傳遞方式捕捉其它所有變數;

[&,a,this]表示以值傳遞的方式捕捉變數a和this,引用傳遞方式捕捉其它所有變數。

不過值得注意的是,捕捉列表不允許變數重複傳遞。下面一些例子就是典型的重複,會導致編譯時期的錯誤。例如:

[=,a]這裡已經以值傳遞方式捕捉了所有變數,但是重複捕捉a了,會報錯的;

[&,&this]這裡&已經以引用傳遞方式捕捉了所有變數,再捕捉this也是一種重複

lambda的使用

對於lambda的使用,說實話,我沒有什麼多說的,個人理解,在沒有lambda之前的c++ , 我們也是那樣好好的使用,並沒有對缺少lambda的c++有什麼抱怨,而現在有了lambda表示式,只是更多的方便了我們去寫**。不知道大家是否記得c++ stl庫中的仿函式物件,仿函式想對於普通函式來說,仿函式可以擁有初始化狀態,而這些初始化狀態是在宣告仿函式物件時,通過引數指定的,一般都是儲存在仿函式物件的私有變數中;在c++中,對於要求具有狀態的函式,我們一般都是使用仿函式來實現,比如以下**:

#includeusing namespace std;

typedef enum

type;

class calc

int operator()(type i)

}private:

int m_x;

int m_y;};

int main()

type;

int main()

};cout《顯而易見的效果,**簡單了,你也少寫了一些**,也去試一試c++中的lambda表示式吧。

關於lambda那些奇葩的東西

#includeusing namespace std;

int main()

; auto by_ref_lambda = [&];

cout<<"by_val_lambda: "《程式輸出結果如下:

by_val_lambda: 11

by_ref_lambda: 11

by_val_lambda: 11

by_ref_lambda: 12

你想到了麼???那這又是為什麼呢?為什麼第三個輸出不是12呢?

在by_val_lambda中,j被視為乙個常量,一旦初始化後不會再改變(可以認為之後只是乙個跟父作用域中j同名的常量),而在by_ref_lambda中,j仍然在使用父作用域中的值。所以,在使用lambda函式的時候,如果需要捕捉的值成為lambda函式的常量,我們通常會使用按值傳遞的方式捕捉;相反的,如果需要捕捉的值成成為lambda函式執行時的變數,則應該採用按引用方式進行捕捉。

再來一段更暈的**:

#includeusing namespace std;

int main()

; wrong!!!

auto mutable_val_lambda = [=]() mutable;

mutable_val_lambda();

cout《這段**主要是用來理解lambda表示式中的mutable關鍵字的。預設情況下,lambda函式總是乙個const函式,mutable可以取消其常量性。按照規定,乙個const的成員函式是不能在函式體內修改非靜態成員變數的值。例如上面的lambda表示式可以看成以下仿函式**:

class const_val_lambda

void operator()() const // 常量成員函式

private:

int val;

};

對於const的成員函式,修改非靜態的成員變數,所以就出錯了。而對於引用的傳遞方式,並不會改變引用本身,而只會改變引用的值,因此就不會報錯了。都是一些糾結的規則。慢慢理解吧。

總結

對於lambda這種東西,有的人用的非常爽,而有的人看著都不爽。仁者見仁,智者見智。不管怎麼樣,作為程式設計師的你,都要會的。這篇文章就是用來彌補自己對c++ lambda表示式的認知不足的過錯,以免以後在別人的**中看到了lambda,還看不懂這種東西,那就丟大人了。

C 中的Lambda表示式詳解

我是搞c 的 一直都在提醒自己,我是搞c 的 但是當c 11出來這麼長時間了,我卻沒有跟著隊伍走,發現很對不起自己的身份,也還好,發現自己也有段時間沒有寫c 了。今天看到了c 中的lambda表示式,雖然用過c 的,但是c 的,一直沒有用,也不知道怎麼用,就可憐的連lambda語法都看不懂。好了,這...

C 中的Lambda表示式詳解

我是搞c 的 一直都在提醒自己,我是搞c 的 但是當c 11出來這麼長時間了,我卻沒有跟著隊伍走,發現很對不起自己的身份,也還好,發現自己也有段時間沒有寫c 了。今天看到了c 中的lambda表示式,雖然用過c 的,但是c 的,一直沒有用,也不知道怎麼用,就可憐的連lambda語法都看不懂。好了,這...

C 中的Lambda表示式詳解

一直都在提醒自己,我是搞c 的 但是當c 11出來這麼長時間了,我卻沒有跟著隊伍走,發現很對不起自己的身份,也還好,發現自己也有段時間沒有寫c 了。今天看到了c 中的lambda表示式,雖然用過c 的,但是c 的,一直沒有用,也不知道怎麼用,就可憐的連lambda語法都看不懂。好了,這裡就對c 中的...