C 11 中的 lambda 函式

2021-09-11 18:05:59 字數 4101 閱讀 7378

λ

​\lambda ​

λ​演算是個非常古老的計算機語言領域的技術,這段歷史一直可以追溯到20世紀30年代。程式語言最先引入 lambda 函式的是 lisp 語言。之後 lisp 的各種變種中都保留了 lambda 函式的概念。

這裡不準備就 lambda 函式的歷史展開介紹。主要是說說 c++11 標準中的 lambda 函式。

c++ 11中的 lambda 函式可以認為就是 functor 的乙個語法糖而已。而且靈活性比 functor 差得多。所以這裡我就用 functor 來講解 lambda 函式。

functor 中文一般翻譯為仿函式,實際上是乙個類。這個類過載了 operator () ,所以對外表現像個普通的函式。比如下面的例子:

class add

};

這個 add 類就是個 functor ,我們可以像個函式那樣使用它。比如

int main()

這個**給 c 賦值的語句中的add(a, b) 看起來很像是個普通函式,但是它其實是個類的成員函式。當然在這個例子中,用個簡單的函式更簡便。但是如果我們的「函式」需要大量的狀態變數。或者我們「函式」介面已經定了——比如這個函式會被傳遞給乙個模板函式中,那麼它的介面就必須是模板函式規定的那樣,無法隨意擴充套件,那麼這時乙個functor 就體現出優勢了。

這裡還是舉個特別的簡單的例子:

class functor

double operator()(double a, double b)

private:

int op;

};

這個例子中 op 就是「函式」 的狀態。通過狀態可以影響函式的行為。

那麼使用它時可以這樣:

int main()

這裡 add 和 sub 都是 functor 的具體例項,乙個實現加法,另乙個實現減法。具體實現的是哪個功能,由初始化物件時確定。

functor 很靈活,但是也挺麻煩。有時我們需要的這個 functor 其實只用一次。為這種只用一次的地方專門寫乙個類有點疊床架屋的感覺。 lambda 函式可以認為就是 functor 的一種簡寫方式。可以讓我們的**更短。當然有利也有弊,lambda 函式只能代替一些簡單的 functor。過於複雜的 functor 用 lambda 函式實現出來是有困難的。

在新版的 c++ 標準中(c++14、c++17 等),lambda 函式的功能有增強。但是本文還是只限於 c++11。

lambda 函式的語法定義如下:

[capture](parameter)mutable -> return-type
capture 是捕捉列表。

parameter 是引數列表,就是函式的那些傳入變數。

mutable 這個後面再介紹。

return-type 返回值的型別,如果返回值明確,也可以省略。

state 是函式體

我們還是用例子來說明,比如最初的那個 add 函式。可以寫為:

auto add = (double x, double y) -> double ;
我們的**可以這樣使用:add(1, 2)。這個**的返回值型別其實是很明確的,所有可以簡寫為:

auto add = (double x, double y) ;
當然並不是什麼時候都可以省略返回值型別的。比如說下面這樣:

auto add = (int x, int y) ->double ;
上面這個**中的 double 就不能省略,因為直接推導出的型別是 int。而我們卻需要 double 型別的返回值。

下面來講解捕捉列表。通過捕捉列表,我們可以獲得 context 中的一些資料。如果沒有捕捉列表我們就只能通過引數傳遞的方式把這些引數傳進來。這裡所謂的 context (上下文) 指的就是 lambda 函式所在的作用域。比如說 lambda 函式是定義在乙個普通函式中的,那麼 context 指的就是這個這個函式作用域。也就是說可以通過捕捉列表獲得所在的這個函式區域性變數的資訊。如果 lambda 函式是定義在乙個類的成員函式中的,那麼除了成員函式區域性變數,還可以獲得 this 指標,間接地也就獲得了這個類的成員變數的資料。

捕捉列表由乙個或多個捕捉項組成,並以逗號分隔,捕捉列表一般有以下幾種形式:

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

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

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

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

下面舉幾個例子:

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

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

[=] 表示值傳遞方式捕捉所有變數。

下面舉個簡單的例子:

int main(int argc, char *ar**)

else

};std::cout << "func(1, 2) = " << func(aa, bb) << std::endl;

cc = 1;

std::cout << "func(1, 2) = " << func(aa, bb) << std::endl;

return 1;

}

輸出的結果如下:

func(1, 2) = -1

func(1, 2) = -1

從上面的例子可以看出,cc 並不是作為引數傳進去的,而是捕捉進去的。那它是什麼時候捕捉進去的呢,是在函式定義時就捕捉的。後面呼叫時不會再次捕捉,所以第二次呼叫時雖然 cc 已經變成 1 了。但是在 lambda 函式中 cc 還是 0。

我們可以用 functor 翻譯這個 lambda 函式:

class functor

double operator() const (double x, double y)

else

}private:

int cc;

};int main(int argc, char *ar**)

這個**可上面的 lambda 函式實現是等價的。實際上現在的 c++ 編譯器就是把 lambda 函式翻譯成 functor 。這裡有個需要注意的地方:

double operator() const (double x, double y)
這一行中有個 const。 說明 operator() 被宣告為 const 函式了。所謂 const 函式就是它不能改變類的狀態。如果我們需要它不是 const 函式就要用到 mutable 關鍵字了。

如果我們需要能夠感知 cc 的變化,可以用引用捕捉的方式。比如下這樣:

int main(int argc, char *ar**)

else

};std::cout << "func(1, 2) = " << func(aa, bb) << std::endl;

cc = 1;

std::cout << "func(1, 2) = " << func(aa, bb) << std::endl;

return 1;

}

這個**的結果:

func(1, 2) = -1

func(1, 2) = 3

翻譯成 fucotor 是下面這樣的:

class functor

double operator() const(double x, double y)

else

}private:

int &cc;

};int main(int argc, char *ar**)

因為 cc 是引用型別,所以即使 operator() 是 const 函式我們也是可以改變 cc 的值的。

關於 lambda 函式,知道這些基本也就夠了。

C 11中的匿名函式(lambda)

c 11提供了對匿名函式的支援,稱為lambda函式 也叫lambda表示式 下面舉了幾個lambda函式的例子 int x,int y 隱式返回型別 int x 沒有return語句 lambda 函式的返回型別是 void 沒有引數,僅訪問某個全域性變數 與上乙個相同,省略了 指定返回型別 in...

c 11匿名函式Lambda

定義乙個lambda函式 auto f capture params opt ret f 呼叫函式其中 capture表示捕獲函式作用域外的外部變數 params是引數 非必須 opt是函式選項,例如可選擇是否允許更改capture到的外部變數 ret表示函式返回型別,一般可以免了,因為我們在f前面...

c 11新特性 lambda函式

lambda歷史悠久,在數理邏輯和電腦科學領域,lambda被用來表示一種匿名函式這種匿名函式代表了一種 演算 lambda calculus 但是在c 領域直到c 11才引入lambda表示式,本文先打算從lambda函式入手 後續會繼續從lambda與仿函式 lambda基礎應用 lambda的...