C++11 學習筆記 lambda表示式

飛翔的黃瓜發表於2017-08-10

寫的不錯的一篇lambda表示式,原連結

http://blog.csdn.net/fjzpdkf/article/details/50249287

  lambda表示式是C++11最重要也最常用的一個特性之一。lambda來源於函數語言程式設計的概念,也是現代程式語言的一個特點。

使用它的原因簡單的說就是,程式設計中提到的 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中使用當前類的成員函式和成員變數。

[cpp] view plain copy
  1. class A  
  2. {  
  3. public:  
  4.     int i_ = 0;  
  5.       
  6.     void func(int x,int y){  
  7.         auto x1 = [] { return i_; };                             //error,沒有捕獲外部變數  
  8.         auto x2 = [=] { return i_ + x + y; };               //OK  
  9.         auto x3 = [&] { return i_ + x + y; };               //OK  
  10.         auto x4 = [this] { return i_; };                        //OK  
  11.         auto x5 = [this] { return i_ + x + y; };            //error,沒有捕獲x,y  
  12.         auto x6 = [this, x, y] { return i_ + x + y; };    //OK  
  13.         auto x7 = [this] { return i_++; };                   //OK  
  14. };  
  15.   
  16. int a=0 , b=1;  
  17. auto f1 = [] { return a; };                            //error,沒有捕獲外部變數      
  18. auto f2 = [&] { return a++ };                      //OK  
  19. auto f3 = [=] { return a; };                         //OK  
  20. auto f4 = [=] {return a++; };                     //error,a是以複製方式捕獲的,無法修改  
  21. auto f5 = [a] { return a+b; };                     //error,沒有捕獲變數b  
  22. auto f6 = [a, &b] { return a + (b++); };      //OK  
  23. auto f7 = [=, &b] { return a + (b++); };     //OK  


 注意的細節:

1.

  一個容易出錯的細節是lambda表示式的延遲呼叫,lambda表示式按值捕獲了所有外部變數。在捕獲的一瞬間,a的值就已經被複制了。如果希望lambda表示式在呼叫時能即時訪問外部變數,我們應當使用引用方式捕獲。

[cpp] view plain copy
  1. int a = 0;  
  2. auto f = [=] { return a; };  
  3.   
  4. a+=1;  
  5.   
  6. cout << f() << endl;       //輸出0  
  7.   
  8.   
  9. int a = 0;  
  10. auto f = [&a] { return a; };  
  11.   
  12. a+=1;  
  13.   
  14. cout << f() <<endl;       //輸出1  


2.

  雖然按值捕獲的變數值均補複製一份儲存在lambda表示式變數中, 修改他們也並不會真正影響到外部,但我們卻仍然無法修改它們。

  那麼如果希望去修改按值捕獲的外部變數,需要顯示指明lambda表示式為mutable。

  需要注意:被mutable修飾的lambda表示式就算沒有引數也要寫明引數列表。
  原因:lambda表示式可以說是就地定義仿函式閉包的“語法糖”。它的捕獲列表捕獲住的任何外部變數,最終均會變為閉包型別的成員變數。按照C++標準,lambda表示式的operator()預設是const的,一個const成員函式是無法修改成員變數的值的。而mutable的作用,就在於取消operator()的const。

[cpp] view plain copy
  1. int a = 0;  
  2. auto f1 = [=] { return a++; };                       //error  
  3. auto f2 = [=] () mutable { return a++; };       //OK  


3.

  沒有捕獲變數的lambda表示式可以直接轉換為函式指標,而捕獲變數的lambda表示式則不能轉換為函式指標。原因可以參考2中的原因。

[cpp] view plain copy
  1. typedef void(*Ptr)(int*);  
  2.   
  3. Ptr p = [](int* p) { delete p; };              //OK  
  4. Ptr p1 = [&] (int* p) { delete p; };         //error  


最後,兩個實際應用到lambda表示式的程式碼。

[cpp] view plain copy
  1. std::vector<int> v = { 1, 2, 3, 4, 5, 6 };  
  2. int even_count = 0;  
  3. for_each(v.begin(), v.end(), [&even_count](int val){  
  4.     if(!(val & 1)){  
  5.         ++ even_count;  
  6.     }  
  7. });  
  8. std::cout << "The number of even is " << even_count << std::endl;  

[cpp] view plain copy
  1. int count = std::count_if( coll.begin(), coll.end(), [](int x){ return x > 10; });  
  2.   
  3. int count = std::count_if( coll.begin(), coll.end(), [](int x){ return x < 10; });  
  4.   
  5. int count = std::count_if( coll.begin(), coll.end(), [](int x){ return x > 5 && x<10; });  

相關文章