http://blog.csdn.net/fjzpdkf/article/details/50249287
lambda表達式是C++11最重要也最常用的一個特性之一。lambda來源于函數式編程的概念,也是現代編程語言的一個特點。
?
一.函數式編程簡介
定義:簡單說,“函數式編程”是一種“編程范式”。它屬于“結構化編程”的一種,主要思想是把運算過程盡量寫成一系列嵌套的函數調用。
?
特點:
1).函數是“第一等公民”,可以賦值給他其他變量,也可以做為參數,返回值。
2).只用“表達式”,不用“語句”。“表達式”是一個單純的運算過程,總是有返回值;“語句”是執行某種操作,沒有返回值。
3).沒有副作用。函數保持獨立,所有功能就是返回一個新的值,其他什么都不做,不修改外部變量的值。
4).引用透明。函數的運行不依賴于外部變量或“狀態”,只依賴于輸入的參數,只要參數相同,返回值就相同。
?
二.lambda表達式
lambda表達式有如下優點:
1).聲明式編程風格:就地匿名定義目標函數或函數對象,不需要額外寫一個命名函數或者函數對象。以更直接的方式去寫程序,好的可讀性和可維護性。
2).簡潔:不需要額外再寫一個函數或者函數對象,避免了代碼膨脹和功能分散,讓開發者更加集中精力在手邊的問題,同時也獲取了更高的生產率。
3).在需要的時間和地點實現功能閉包,使程序更靈活。
?
lambda表達式的語法歸納如下:
[ caputrue ] ( params ) opt -> ret { body; };
1).capture是捕獲列表;
2).params是參數表;(選填)
3).opt是函數選項;可以填mutable,exception,attribute(選填)
mutable說明lambda表達式體內的代碼可以修改被捕獲的變量,并且可以訪問被捕獲的對象的non-const方法。
exception說明lambda表達式是否拋出異常以及何種異常。
attribute用來聲明屬性。
4).ret是返回值類型。(選填)
5).body是函數體。
?
捕獲列表:lambda表達式的捕獲列表精細控制了lambda表達式能夠訪問的外部變量,以及如何訪問這些變量。
1).[]不捕獲任何變量。
2).[&]捕獲外部作用域中所有變量,并作為引用在函數體中使用(按引用捕獲)。
3).[=]捕獲外部作用域中所有變量,并作為副本在函數體中使用(按值捕獲)。
4).[=,&foo]按值捕獲外部作用域中所有變量,并按引用捕獲foo變量。
5).[bar]按值捕獲bar變量,同時不捕獲其他變量。
6).[this]捕獲當前類中的this指針,讓lambda表達式擁有和當前類成員函數同樣的訪問權限。如果已經使用了&或者=,就默認添加此選項。捕獲this的目的是可以在lamda中使用當前類的成員函數和成員變量。
- class?A??
- {??
- public:??
- ????int?i_?=?0;??
- ??????
- ????void?func(int?x,int?y){??
- ????????auto?x1?=?[]?{?return?i_;?};???????????????????????????????
- ????????auto?x2?=?[=]?{?return?i_?+?x?+?y;?};?????????????????
- ????????auto?x3?=?[&]?{?return?i_?+?x?+?y;?};?????????????????
- ????????auto?x4?=?[this]?{?return?i_;?};??????????????????????????
- ????????auto?x5?=?[this]?{?return?i_?+?x?+?y;?};??????????????
- ????????auto?x6?=?[this,?x,?y]?{?return?i_?+?x?+?y;?};??????
- ????????auto?x7?=?[this]?{?return?i_++;?};?????????????????????
- };??
- ??
- int?a=0?,?b=1;??
- auto?f1?=?[]?{?return?a;?};??????????????????????????????
- auto?f2?=?[&]?{?return?a++?};????????????????????????
- auto?f3?=?[=]?{?return?a;?};???????????????????????????
- auto?f4?=?[=]?{return?a++;?};???????????????????????
- auto?f5?=?[a]?{?return?a+b;?};???????????????????????
- auto?f6?=?[a,?&b]?{?return?a?+?(b++);?};????????
- auto?f7?=?[=,?&b]?{?return?a?+?(b++);?};???????
?注意的細節:
1.
一個容易出錯的細節是lambda表達式的延遲調用,lambda表達式按值捕獲了所有外部變量。在捕獲的一瞬間,a的值就已經被復制了。如果希望lambda表達式在調用時能即時訪問外部變量,我們應當使用引用方式捕獲。
- int?a?=?0;??
- auto?f?=?[=]?{?return?a;?};??
- ??
- a+=1;??
- ??
- cout?<<?f()?<<?endl;?????????
- ??
- ??
- int?a?=?0;??
- auto?f?=?[&a]?{?return?a;?};??
- ??
- a+=1;??
- ??
- cout?<<?f()?<<endl;?????????
2.
雖然按值捕獲的變量值均補復制一份存儲在lambda表達式變量中, 修改他們也并不會真正影響到外部,但我們卻仍然無法修改它們。
那么如果希望去修改按值捕獲的外部變量,需要顯示指明lambda表達式為mutable。
需要注意:被mutable修飾的lambda表達式就算沒有參數也要寫明參數列表。
原因:lambda表達式可以說是就地定義仿函數閉包的“語法糖”。它的捕獲列表捕獲住的任何外部變量,最終均會變為閉包類型的成員變量。按照C++標準,lambda表達式的operator()默認是const的,一個const成員函數是無法修改成員變量的值的。而mutable的作用,就在于取消operator()的const。
- int?a?=?0;??
- auto?f1?=?[=]?{?return?a++;?};?????????????????????????
- auto?f2?=?[=]?()?mutable?{?return?a++;?};?????????
3.
沒有捕獲變量的lambda表達式可以直接轉換為函數指針,而捕獲變量的lambda表達式則不能轉換為函數指針。原因可以參考2中的原因。
- typedef?void(*Ptr)(int*);??
- ??
- Ptr?p?=?[](int*?p)?{?delete?p;?};????????????????
- Ptr?p1?=?[&]?(int*?p)?{?delete?p;?};???????????
最后,兩個實際應用到lambda表達式的代碼。
- std::vector<int>?v?=?{?1,?2,?3,?4,?5,?6?};??
- int?even_count?=?0;??
- for_each(v.begin(),?v.end(),?[&even_count](int?val){??
- ????if(!(val?&?1)){??
- ????????++?even_count;??
- ????}??
- });??
- std::cout?<<?"The?number?of?even?is?"?<<?even_count?<<?std::endl;??
- int?count?=?std::count_if(?coll.begin(),?coll.end(),?[](int?x){?return?x?>?10;?});??
- ??
- int?count?=?std::count_if(?coll.begin(),?coll.end(),?[](int?x){?return?x?<?10;?});??
- ??
- int?count?=?std::count_if(?coll.begin(),?coll.end(),?[](int?x){?return?x?>?5?&&?x<10;?});??