lambda表达式相关
轉載自:? ?http://www.jb51.net/article/56147.htm
?
今天就直接點兒,從代碼擼起走,,
復制代碼代碼如下:
#include<iostream>
using namespace std;
?
int main()
{
??? int a = 1;
??? int b = 2;
?
??? auto func = [=, &b](int c)->int {return b += a + c;};
??? return 0;
}
?
?
當我第一次看到這段代碼時,我直接凌亂了,直接看不懂啊。上面這段代碼,如果你看懂了,下面的內容就當時復習了;如果看不懂了,就接著和我一起總結吧。
基本語法
簡單來說,Lambda函數也就是一個函數,它的語法定義如下:
?
復制代碼代碼如下:
[capture](parameters) mutable ->return-type{statement}
?
1.[capture]:捕捉列表。捕捉列表總是出現在Lambda函數的開始處。實際上,[]是Lambda引出符。編譯器根據該引出符判斷接下來的代碼是否是Lambda函數。捕捉列表能夠捕捉上下文中的變量以供Lambda函數使用;
2.(parameters):參數列表。與普通函數的參數列表一致。如果不需要參數傳遞,則可以連同括號“()”一起省略;
3.mutable:mutable修飾符。默認情況下,Lambda函數總是一個const函數,mutable可以取消其常量性。在使用該修飾符時,參數列表不可省略(即使參數為空);
4.->return-type:返回類型。用追蹤返回類型形式聲明函數的返回類型。我們可以在不需要返回值的時候也可以連同符號”->”一起省略。此外,在返回類型明確的情況下,也可以省略該部分,讓編譯器對返回類型進行推導;
5.{statement}:函數體。內容與普通函數一樣,不過除了可以使用參數之外,還可以使用所有捕獲的變量。
與普通函數最大的區別是,除了可以使用參數以外,Lambda函數還可以通過捕獲列表訪問一些上下文中的數據。具體地,捕捉列表描述了上下文中哪些數據可以被Lambda使用,以及使用方式(以值傳遞的方式或引用傳遞的方式)。語法上,在“[]”包括起來的是捕捉列表,捕捉列表由多個捕捉項組成,并以逗號分隔。捕捉列表有以下幾種形式:
?
1.[var]表示值傳遞方式捕捉變量var; 2.[=]表示值傳遞方式捕捉所有父作用域的變量(包括this); 3.[&var]表示引用傳遞捕捉變量var; 4.[&]表示引用傳遞方式捕捉所有父作用域的變量(包括this); 5.[this]表示值傳遞方式捕捉當前的this指針。?
1).[]不捕獲任何變量。 2).[&]捕獲外部作用域中所有變量,并作為引用在函數體中使用(按引用捕獲)。 3).[=]捕獲外部作用域中所有變量,并作為副本在函數體中使用(按值捕獲)。 4).[=,&foo]按值捕獲外部作用域中所有變量,并按引用捕獲foo變量。 5).[bar]按值捕獲bar變量,同時不捕獲其他變量。 6).[this]捕獲當前類中的this指針,讓lambda表達式擁有和當前類成員函數同樣的訪問權限。如果已經使用了&或者=,就默認添加此選項。捕獲this的目的是可以在lamda中使用當前類的成員函數和成員變量。上面提到了一個父作用域,也就是包含Lambda函數的語句塊,說通俗點就是包含Lambda的“{}”代碼塊。上面的捕捉列表還可以進行組合,例如:
1.[=,&a,&b]表示以引用傳遞的方式捕捉變量a和b,以值傳遞方式捕捉其它所有變量;
2.[&,a,this]表示以值傳遞的方式捕捉變量a和this,引用傳遞方式捕捉其它所有變量。
不過值得注意的是,捕捉列表不允許變量重復傳遞。下面一些例子就是典型的重復,會導致編譯時期的錯誤。例如:
3.[=,a]這里已經以值傳遞方式捕捉了所有變量,但是重復捕捉a了,會報錯的;
4.[&,&this]這里&已經以引用傳遞方式捕捉了所有變量,再捕捉this也是一種重復。
Lambda的使用
對于Lambda的使用,說實話,我沒有什么多說的,個人理解,在沒有Lambda之前的C++ , 我們也是那樣好好的使用,并沒有對缺少Lambda的C++有什么抱怨,而現在有了Lambda表達式,只是更多的方便了我們去寫代碼。不知道大家是否記得C++ STL庫中的仿函數對象,仿函數想對于普通函數來說,仿函數可以擁有初始化狀態,而這些初始化狀態是在聲明仿函數對象時,通過參數指定的,一般都是保存在仿函數對象的私有變量中;在C++中,對于要求具有狀態的函數,我們一般都是使用仿函數來實現,比如以下代碼:
?
復制代碼代碼如下:
#include<iostream>
using namespace std;
?
typedef enum
{
??? add = 0,
??? sub,
??? mul,
??? divi
}type;
?
class Calc
{
??? public:
??????? Calc(int x, int y):m_x(x), m_y(y){}
?
??????? int operator()(type i)
??????? {
??????????? switch (i)
??????????? {
??????????????? case add:
??????????????????? return m_x + m_y;
??????????????? case sub:
??????????????????? return m_x - m_y;
??????????????? case mul:
??????????????????? return m_x * m_y;
??????????????? case divi:
??????????????????? return m_x / m_y;
??????????? }
??????? }
?
??? private:
??????? int m_x;
??????? int m_y;
};
?
int main()
{
??? Calc addObj(10, 20);
??? cout<<addObj(add)<<endl; // 發現C++11中,enum類型的使用也變了,更“強”了??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??? return 0;
}
?
現在我們有了Lambda這個利器,那是不是可以重寫上面的實現呢?看代碼:
?
復制代碼代碼如下:
#include<iostream>
using namespace std;
??????
typedef enum
{?????
??? add = 0,
??? sub,
??? mul,
??? divi
}type;
??????
int main()
{?????
??? int a = 10;
??? int b = 20;
??????
??? auto func = [=](type i)->int {
??????? switch (i)
??????? {
??????????? case add:
??????????????? return a + b;
??????????? case sub:
??????????????? return a - b;
??????????? case mul:
??????????????? return a * b;
??????????? case divi:
??????????????? return a / b;
??????? }
??? };
??????
??? cout<<func(add)<<endl;
}
?
顯而易見的效果,代碼簡單了,你也少寫了一些代碼,也去試一試C++中的Lambda表達式吧。
關于Lambda那些奇葩的東西
看以下一段代碼:
復制代碼代碼如下:
#include<iostream>?????????
using namespace std;???????
???????????????????????????
int main()?????????????????
{??????????????????????????
??? int j = 10;????????????
??? auto by_val_lambda = [=]{ return j + 1; };
??? auto by_ref_lambda = [&]{ return j + 1; };
??? cout<<"by_val_lambda: "<<by_val_lambda()<<endl;
??? cout<<"by_ref_lambda: "<<by_ref_lambda()<<endl;
???????????????????????????
??? ++j;???????????????????
??? cout<<"by_val_lambda: "<<by_val_lambda()<<endl;
??? cout<<"by_ref_lambda: "<<by_ref_lambda()<<endl;
???????????????????????????
??? return 0;??????????????
}
?
程序輸出結果如下:
復制代碼代碼如下:
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函數運行時的變量,則應該采用按引用方式進行捕捉。
再來一段更暈的代碼:
?
復制代碼代碼如下:
#include<iostream>??????????????????
using namespace std;????????????????
????????????????????????????????????
int main()??????????????????????????
{???????????????????????????????????
??? int val = 0;????????????????????????????????????
??? // auto const_val_lambda = [=](){ val = 3; }; wrong!!!
????????????????????????????????????
??? auto mutable_val_lambda = [=]() mutable{ val = 3; };
??? mutable_val_lambda();???????????
??? cout<<val<<endl; // 0
????????????????????????????????????
??? auto const_ref_lambda = [&]() { val = 4; };
??? const_ref_lambda();?????????????
??? cout<<val<<endl; // 4
????????????????????????????????????
??? auto mutable_ref_lambda = [&]() mutable{ val = 5; };
??? mutable_ref_lambda();???????????
??? cout<<val<<endl; // 5
????????????????????????????????????
??? return 0;??????
}
?
這段代碼主要是用來理解Lambda表達式中的mutable關鍵字的。默認情況下,Lambda函數總是一個const函數,mutable可以取消其常量性。按照規定,一個const的成員函數是不能在函數體內修改非靜態成員變量的值。例如上面的Lambda表達式可以看成以下仿函數代碼:
?
復制代碼代碼如下:
class const_val_lambda
{
public:
??? const_val_lambda(int v) : val(v) {}
??? void operator()() const { val = 3; } // 常量成員函數
?
private:
??? int val;
};
?
對于const的成員函數,修改非靜態的成員變量,所以就出錯了。而對于引用的傳遞方式,并不會改變引用本身,而只會改變引用的值,因此就不會報錯了。都是一些糾結的規則。慢慢理解吧。
總結
以上是生活随笔為你收集整理的lambda表达式相关的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一切从零开始外挂教程(翻录)
- 下一篇: 遍历磁盘根目录