天天看點

c++11 lambda

為什麼需要lambda函數

匿名函數是許多程式設計語言都支援的概念,有函數體,沒有函數名。1958年,lisp首先采用匿名函數,匿名函數最常用的是作為回調函數的值。正因為有這樣的需求,c++引入了lambda 函數,你可以在你的源碼中内聯一個lambda函數,這就使得建立快速的,一次性的函數變得簡單了。例如,你可以把lambda函數可在參數中傳遞給std::sort函數

#include <algorithm>

#include <cmath>

void abssort(float* x, unsigned N) {

    std::sort(x, x + N,

        // Lambda expression begins

        [](float a, float b) {

            return std::abs(a) < std::abs(b);

        });

}

你可能會問,使用函數對象不是也可以嗎?是的,函數對象當然沒問題,自己寫的回調函數,你可以傳個函數指針也沒有問題。他們有優點也有缺點。函數對象能維護狀态,但文法開銷大,而函數指針文法開銷小,卻沒法儲存範圍内的狀态。如果你覺得魚和熊掌不可兼得,那你可錯了。lambda函數結合了兩者的優點,讓你寫出優雅簡潔的代碼。

基本lambda文法

基本形式如下:

[capture](parameters)->return-type {body}

[]叫做捕獲說明符,表示一個lambda表達式的開始。接下來是參數清單,即這個匿名的lambda函數的參數,->return-type表示傳回類型,如果沒有傳回類型,則可以省略這部分。想知道為什麼傳回類型可以這麼表示,這涉及到c++11的另一特性,參見自動類型推導,最後就是函數體部分了。

我們可以這樣輸出"hello,world"

auto func = [] () { cout << "hello,world"; };

func(); // now call the function

變量捕獲與lambda閉包實作

string name;

cin >> name;

[&](){cout << name;}();

lambda函數能夠捕獲lambda函數外的具有自動存儲時期的變量。函數體與這些變量的集合合起來叫閉包。

  • [] 不截取任何變量
  • [&} 截取外部作用域中所有變量,并作為引用在函數體中使用
  • [=] 截取外部作用域中所有變量,并拷貝一份在函數體中使用
  • [=, &foo] 截取外部作用域中所有變量,并拷貝一份在函數體中使用,但是對foo變量使用引用
  • [bar] 截取bar變量并且拷貝一份在函數體重使用,同時不截取其他變量
  • [x, &y] x按值傳遞,y按引用傳遞
  • [this] 截取目前類中的this指針。如果已經使用了&或者=就預設添加此選項。

看到這,不禁要問,這魔法般的變量捕獲是怎麼實作的呢?原來,lambda是通過建立個小類來實作的。這個類重載了操作符(),一個lambda函數是該類的一個執行個體。當該類被構造時,周圍的變量就傳遞給構造函數并以成員變量儲存起來。看起來跟函數對象很相似。

最後,lambda函數的類型是什麼呢,答案是std:function。

C++11 的 lambda 表達式規範如下:

[

 capture 

]

(

 params 

)

 mutable exception attribute 

->

 ret 

{

 body 

}

(1)

[

]

(

)

->

{

}

(2)

[

]

(

)

{

}

(3)

[

]

{

}

(4)

其中

  • (1) 是完整的 lambda 表達式形式,
  • (2) const 類型的 lambda 表達式,該類型的表達式不能改捕獲("capture")清單中的值。
  • (3)省略了傳回值類型的 lambda 表達式,但是該 lambda 表達式的傳回類型可以按照下列規則推演出來:
    • 如果 lambda 代碼塊中包含了 return 語句,則該 lambda 表達式的傳回類型由 return 語句的傳回類型确定。
    • 如果沒有 return 語句,則類似 void f(...) 函數。
  • 省略了參數清單,類似于無參函數 f()。

mutable 修飾符說明 lambda 表達式體内的代碼可以修改被捕獲的變量,并且可以通路被捕獲對象的 non-const 方法。

exception 說明 lambda 表達式是否抛出異常(

noexcept

),以及抛出何種異常,類似于void f() throw(X, Y)。

  • [a,&b]

     a變量以值的方式呗捕獲,b以引用的方式被捕獲。
  • [this]

     以值的方式捕獲 this 指針。
  • [&]

     以引用的方式捕獲所有的外部自動變量。
  • [=]

     以值的方式捕獲所有的外部自動變量。
  • []

     不捕獲外部的任何變量。

繼續閱讀