C++11 lambda函數(shù)

匿名函數(shù)是許多編程語言都支持的概念,有函數(shù)體,沒有函數(shù)名。1958年,lisp首先采用匿名函數(shù),匿名函數(shù)最常用的是作為回調(diào)函數(shù)的值。正因?yàn)橛羞@樣的需求,c++引入了lambda 函數(shù),你可以在你的源碼中內(nèi)聯(lián)一個(gè)lambda函數(shù),這就使得創(chuàng)建快速的,一次性的函數(shù)變得簡單了。例如,你可以把lambda函數(shù)可在參數(shù)中傳遞給std::sort函數(shù)。

#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);
        });
}

你可能會問,使用函數(shù)對象不是也可以嗎?是的,函數(shù)對象當(dāng)然沒問題,自己寫的回調(diào)函數(shù),你可以傳個(gè)函數(shù)指針也沒有問題。他們有優(yōu)點(diǎn)也有缺點(diǎn)。函數(shù)對象能維護(hù)狀態(tài),但語法開銷大,而函數(shù)指針語法開銷小,卻沒法保存范圍內(nèi)的狀態(tài)。如果你覺得魚和熊掌不可兼得,那你可錯(cuò)了。lambda函數(shù)結(jié)合了兩者的優(yōu)點(diǎn),讓你寫出優(yōu)雅簡潔的代碼。

基本lambda語法
基本形式如下:

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

capture 指定了在可見域范圍內(nèi) lambda 表達(dá)式的代碼內(nèi)可見得外部變量的列表,具體解釋如下:
[a,&b] a變量以值的方式唄捕獲,b以引用的方式被捕獲。
[this] 以值的方式捕獲 this 指針。
[&] 以引用的方式捕獲所有的外部自動變量。
[=] 以值的方式捕獲所有的外部自動變量。
[] 不捕獲外部的任何變量。

params 指定 lambda 表達(dá)式的參數(shù)。

mutable 修飾符說明 lambda 表達(dá)式體內(nèi)的代碼可以修改被捕獲的變量,并且可以訪問被捕獲對象的 non-const 方法。
exception 說明 lambda 表達(dá)式是否拋出異常(noexcept),以及拋出何種異常,類似于void f() throw(X, Y)。
attribute 用來聲明屬性

->return-type表示返回類型,如果沒有返回類型,則可以省略這部分。如果 lambda 代碼塊中包含了 return 語句,則該 lambda 表達(dá)式的返回類型由 return 語句的返回類型確定。

lambda函數(shù)的類型是std:function,并非函數(shù)指針。不過C++11標(biāo)準(zhǔn)允許lambda函數(shù)向函數(shù)指針的轉(zhuǎn)換,但前提是lambda函數(shù)沒有捕捉任何變量,且函數(shù)指針?biāo)镜暮瘮?shù)原型,必須跟lambda函數(shù)有著相同的調(diào)用形式。

// 類型為function<int(int,int)>
auto add [](int x, int y) {return x+y;}
add(1,2)  // 3

除去語法層面的不同,lambda和仿函數(shù)有著相同的內(nèi)涵——都可以捕捉一些變量作為初始狀態(tài),并接受參數(shù)進(jìn)行運(yùn)算。而事實(shí)上,仿函數(shù)是編譯器實(shí)現(xiàn)lambda的一種方式。在現(xiàn)階段,編譯器都會把lambda函數(shù)轉(zhuǎn)化為一個(gè)仿函數(shù)對象。因此,在C++11中,lambda可以視為仿函數(shù)的一種等價(jià)形式,或者稱為仿函數(shù)的“語法甜點(diǎn)”。

lambda函數(shù)在C++11標(biāo)準(zhǔn)中默認(rèn)是內(nèi)聯(lián)的。

使用lambda函數(shù)時(shí),捕獲列表按值傳遞和按引用傳遞的效果是不一樣的。對于按值傳遞的捕獲列表,其傳遞的值在lambda函數(shù)定義的時(shí)候就已經(jīng)決定了。而按引用傳遞的捕獲列表,其傳遞的值則等于lambda函數(shù)調(diào)用時(shí)的值。

int i = 1;
auto by_val_lambda = [=] { return j+1;}
auto by_ref_lambda = [&] { return j+1;}
cout<<"by_val_lambda: "<<by_val_lambda()<<endl; //12
cout<<"by_ref_lambda: "<<by_ref_lambda()<<endl; //12

i++;
cout<<"by_val_lambda: "<<by_val_lambda()<<endl; //12
cout<<"by_ref_lambda: "<<by_ref_lambda()<<endl; //13

如果需要捕捉的值稱為lambda函數(shù)的常量,我們通常會使用按值傳遞的方式捕捉;而需要捕捉的值稱為lambda函數(shù)運(yùn)行時(shí)的變量(類似于參數(shù)的效果),則應(yīng)采用按引用傳遞的方式進(jìn)行捕捉。

lambda函數(shù)的使用場景

  1. 一些短小的函數(shù),但只使用一次的。
  2. 配合STL泛型算法使用。
  3. 回調(diào)函數(shù)。

lambda函數(shù)被設(shè)計(jì)的目的,就是要就地書寫,就地使用。使用lambda的用戶,更傾向于在一個(gè)屏幕里看到所有的代碼,而不是依靠代碼瀏覽工具在文件間找到函數(shù)的實(shí)現(xiàn)。而在封裝的思維層面上,lambda只是一種局部的封裝,以及局部的共享。而需要全局共享的代碼邏輯,我們還是需要用函數(shù)(無狀態(tài))或者仿函數(shù)(有初始狀態(tài))封裝起來。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容