欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文讀懂c++11 Lambda表達(dá)式

 更新時間:2020年08月12日 09:29:31   作者:Dabelv  
這篇文章主要介紹了c++11 Lambda表達(dá)式的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)C++,感興趣的朋友可以了解下

1.簡介

1.1定義

C++11新增了很多特性,Lambda表達(dá)式(Lambda expression)就是其中之一,很多語言都提供了 Lambda 表達(dá)式,如 Python,Java ,C#等。本質(zhì)上, Lambda 表達(dá)式是一個可調(diào)用的代碼單元[1]^{[1]}[1]。實際上是一個閉包(closure),類似于一個匿名函數(shù),擁有捕獲所在作用域中變量的能力,能夠?qū)⒑瘮?shù)做為對象一樣使用,通常用來實現(xiàn)回調(diào)函數(shù)、代理等功能。Lambda表達(dá)式是函數(shù)式編程的基礎(chǔ),C++11引入了Lambda則彌補(bǔ)了C++在函數(shù)式編程方面的空缺。

1.2作用

以往C++需要傳入一個函數(shù)的時候,必須事先進(jìn)行聲明,視情況可以聲明為一個普通函數(shù)然后傳入函數(shù)指針,或者聲明一個仿函數(shù)(functor,函數(shù)對象),然后傳入一個對象。比如C++的STL中很多算法函數(shù)模板需要傳入謂詞(predicate)來作為判斷條件,如排序算法sort。謂詞就是一個可調(diào)用的表達(dá)式,其返回結(jié)果是一個能用作條件的值。標(biāo)準(zhǔn)庫算法所使用的謂詞分為兩類:一元謂詞(unary predicate,只接受單一參數(shù))和二元謂詞(binary predicate,接受兩個參數(shù))。接受謂詞的算法對輸入序列中的元素調(diào)用謂詞,因此元素類型必須能轉(zhuǎn)換為謂詞的參數(shù)類型。如下面使用sort()傳入比較函數(shù)shorter()(這里的比較函數(shù)shorter()就是謂詞)將字符串按長度由短至長排列。

//謂詞:比較函數(shù),用來按長度排列字符串
bool shorter(const string& s1,const string& s2)
{
 return s1.size()<s2.size();
}

//按長度由短至長排列words
std::sort(words.begin(),words.end(),shorter);

Lambda表達(dá)式可以像函數(shù)指針、仿函數(shù)一樣,作為一個可調(diào)用對象(callable object)被使用,比如作為謂詞傳入標(biāo)準(zhǔn)庫算法。

也許有人會問,有了函數(shù)指針、函數(shù)對象為何還要引入Lambda呢?函數(shù)對象能維護(hù)狀態(tài),但語法開銷大,而函數(shù)指針語法開銷小,卻沒法保存函數(shù)體內(nèi)的狀態(tài)。如果你覺得魚和熊掌不可兼得,那你可錯了。Lambda函數(shù)結(jié)合了兩者的優(yōu)點(diǎn),讓你寫出優(yōu)雅簡潔的代碼。

1.3語法格式

Lambda 表達(dá)式就是一個可調(diào)用的代碼單元,我們可以將其理解為一個未命名的內(nèi)聯(lián)函數(shù)。與任何函數(shù)類似,一個Lambda具有一個返回類型、一個參數(shù)列表和一個函數(shù)體。但與函數(shù)不同,Lambda可以定義在函數(shù)內(nèi)部,其語法格式如下:

[capture list](parameter list) mutable(可選) 異常屬性->return type{function body}

capture list(捕獲列表)是一個Lambda所在函數(shù)中定義的局部變量的列表,通常為空,表示Lambda不使用它所在函數(shù)中的任何局部變量。parameter list(參數(shù)列表)、return type(返回類型)、function body(函數(shù)體)與任何普通函數(shù)基本一致,但是Lambda的參數(shù)列表不能有默認(rèn)參數(shù),且必須使用尾置返回類型。 mutable表示Lambda能夠修改捕獲的變量,省略了mutable,則不能修改。異常屬性則指定Lambda可能會拋出的異常類型。

其中Lambda表達(dá)式必須的部分只有capture list和function body。在Lambda忽略參數(shù)列表時表示指定一個空參數(shù)列表,忽略返回類型時,Lambda可根據(jù)函數(shù)體中的代碼推斷出返回類型。例如:

auto f=[]{return 42;}

我們定義了一個可調(diào)用對象f,它不接受任何參數(shù),返回42。auto關(guān)鍵字實際會將 Lambda 表達(dá)式轉(zhuǎn)換成一種類似于std::function的內(nèi)部類型(但并不是std::function類型,雖然與std::function“兼容”)。所以,我們也可以這么寫:

std::function<int()> Lambda = [] () -> int { return val * 100;};

如果你對std::function<int()>這種寫法感到很神奇,可以查看 C++ 11 的有關(guān)std::function的用法。簡單來說,std::function<int()>是一個實例化后的模板類,代表一個可調(diào)用的對象,接受 0 個參數(shù),返回值是int。所以,當(dāng)我們需要一個接受一個double作為參數(shù),返回int的對象時,就可以寫作:std::function<int(double)>[3]^{[3]}[3]。

1.4調(diào)用方式

Lambda表達(dá)式的調(diào)用方式與普通函數(shù)的調(diào)用方式相同,上面Lambda表達(dá)式的調(diào)用方式如下:

cout<<f()<<endl;  //打印42

//或者直接調(diào)用
cout<<[]{return 42;}()<<endl;

我們還可以定義一個單參數(shù)的Lambda,實現(xiàn)上面字符串排序的shorter()比較函數(shù)的功能:

auto f=[](cosnt string& a,const string& b)
{
 return a.size()<b.size();
}

//將Lambda傳入排序算法sort中
sort(words.begin(),word2.end(),[](cosnt string& a,const string& b){
 return a.size()<b.size();
});

//或者
sort(words.begin(),word2.end(),f);

2.Lambda的捕獲列表

Lambda可以獲?。ú东@)它所在作用域中的變量值,由捕獲列表(capture list)指定在Lambda 表達(dá)式的代碼內(nèi)可使用的外部變量。比如雖然一個Lambda可以出現(xiàn)在一個函數(shù)中,使用其局部變量,但它只能使用那些在捕獲列表中明確指明的變量。Lambda在捕獲所需的外部變量有兩種方式:引用和值。我們可以在捕獲列表中設(shè)置各變量的捕獲方式。如果沒有設(shè)置捕獲列表,Lambda默認(rèn)不能捕獲任何的變量。捕獲方式具體有如下幾種:

  • [] 不截取任何變量
  • [&} 截取外部作用域中所有變量,并作為引用在函數(shù)體中使用
  • [=] 截取外部作用域中所有變量,并拷貝一份在函數(shù)體中使用
  • [=,&valist]   截取外部作用域中所有變量,并拷貝一份在函數(shù)體中使用,但是對以逗號分隔valist使用引用
  • [&,valist] 以引用的方式捕獲外部作用域中所有變量,對以逗號分隔的變量列表valist使用值的方式捕獲
  • [valist] 對以逗號分隔的變量列表valist使用值的方式捕獲
  • [&valist] 對以逗號分隔的變量列表valist使用引用的方式捕獲
  • [this] 截取當(dāng)前類中的this指針。如果已經(jīng)使用了&或者=就默認(rèn)添加此選項。

在[]中設(shè)置捕獲列表,就可以在Lambda中使用變量a了,這里使用按值(=, by value)捕獲。

#include <iostream>

int main()
{
 int a = 123;
 auto lambda = [=]()->void
 {
 std::cout << "In Lambda: " << a << std::endl;
 };
 lambda();
 return 0;
}

編譯運(yùn)行結(jié)果如下:

In Lambda: 123

按值傳遞到Lambda中的變量,默認(rèn)是不可變的(immutable),如果需要在Lambda中進(jìn)行修改的話,需要在形參列表后添加mutable關(guān)鍵字(按值傳遞無法改變Lambda外變量的值)。

#include <iostream>
int main()
{
 int a = 123;
 std::cout << a << std::endl;
 auto lambda = [=]() mutable ->void{
 a = 234;
 std::cout << "In Lambda: " << a << std::endl;
 };
 lambda();
 std::cout << a << std::endl;
 return 0;
}

編譯運(yùn)行結(jié)果為:

123
In Lambda: 234  //可以修改
123             //注意這里的值,并沒有改變

如果沒有添加mutable,則編譯出錯:

$ g++ main.cpp -std=c++11
main.cpp:9:5: error: cannot assign to a variable captured by copy in a non-mutable Lambda
    a = 234;
                ~ ^
1 error generated.

看到這,不禁要問,這魔法般的變量捕獲是怎么實現(xiàn)的呢?原來,Lambda是通過創(chuàng)建個類來實現(xiàn)的。這個類重載了操作符(),一個Lambda函數(shù)是該類的一個實例。當(dāng)該類被構(gòu)造時,周圍的變量就傳遞給構(gòu)造函數(shù)并以成員變量保存起來,看起來跟函數(shù)對象(仿函數(shù))很相似,但是C++11標(biāo)準(zhǔn)建議使用Lambda表達(dá)式,而不是函數(shù)對象,Lambda表達(dá)式更加輕量高效,易于使用和理解[4]^{[4]}[4]。

3.Lambda的類型

lambda函數(shù)的類型看起來和函數(shù)指針很像,都是把函數(shù)賦值給了一個變量。實際上,lambda函數(shù)是用仿函數(shù)實現(xiàn)的,它看起來又像是一種自定義的類。而事實上,lambda類型并不是簡單的函數(shù)指針類型或者自定義類型,lambda函數(shù)是一個閉包(closure)的類,C++11標(biāo)準(zhǔn)規(guī)定,closure類型是特有的、匿名且非聯(lián)合體的class類型。每個lambda表達(dá)式都會產(chǎn)生一個閉包類型的臨時對象(右值)。因此,嚴(yán)格來說,lambda函數(shù)并非函數(shù)指針,但是C++11允許lambda表達(dá)式向函數(shù)指針轉(zhuǎn)換,前提是沒有捕捉任何變量且函數(shù)指針?biāo)赶虻暮瘮?shù)必須跟lambda函數(shù)有相同的調(diào)用方式。

typedef int(*pfunc)(int x, int y);

int main()
{
 auto func = [](int x, int y)->int {
 return x + y;
 };
 pfunc p1 = nullptr;
 p1 = func;  //lambda表達(dá)式向函數(shù)指針轉(zhuǎn)換

 std::cout << p1(1, 2) << std::endl;

 return 0;
}

4.lambda的常量性和mutable關(guān)鍵字

C++11中,默認(rèn)情況下lambda函數(shù)是一個const函數(shù),按照規(guī)則,一個const成員函數(shù)是不能在函數(shù)體內(nèi)改變非靜態(tài)成員變量的值。

int main()
{
 int val = 0;
 auto const_val_lambda = [=] { val = 3; }; // 編譯失敗,不能在const的lambda函數(shù)中修改按值捕獲的變量val

 auto mutable_val_lambda = [=]() mutable { val = 3; };

 auto const_ref_lambda = [&] { val = 3; };

 auto const_param_lambda = [](int v) { v = 3; };
 const_param_lambda(val);

 return 0;
}

閱讀代碼,注意以下幾點(diǎn):
 (1)可以看到在const的lambda函數(shù)中無法修改按值捕捉到的變量。lambda函數(shù)是通過仿函數(shù)來實現(xiàn)的,捕捉到的變量相當(dāng)于是仿函數(shù)類中的成員變量,而lambda函數(shù)相當(dāng)于是成員函數(shù),const成員函數(shù)自然不能修改普通成員變量;
 (2)使用引用的方式捕獲的變量在常量成員函數(shù)中值被更改則不會導(dǎo)致錯誤,其原因簡單地說,由于const_ref_lambda 不會改變引用本身,而只會改變引用的值,所以編譯通過;
 (3)使用mutable修飾的mutable_val_lambda,去除了const屬性,所以可以修改按值方式捕獲到的變量;
 (4)按值傳遞參數(shù)的const_param_lambda修改的是傳入lambda函數(shù)的實參,當(dāng)然不會有問題。

5.Lambda的常見用法

(1)Lambda函數(shù)和STL
 Lambda函數(shù)的引入為STL的使用提供了極大的方便。比如下面這個例子,當(dāng)你想遍歷一個vector的時候,原來你得這么寫:

vector<int> v={1,2,3,4,5,6,7,8,9};

//傳統(tǒng)的for循環(huán)
for ( auto itr = v.begin(), end = v.end(); itr != end; itr++ )
{ 
 cout << *itr; 
}

//函數(shù)指針
void printFunc(int v)
{
 cout<<v;
}
for_each(v.begin(),v.end(),printFunc);

//仿函數(shù)
struct CPrintFunc
{
 void operator() (int val)const { cout << val; }
};
for_each(v.begin(),v.end(),CPrintFunc());

現(xiàn)在有了Lambda函數(shù)你就可以這么寫:

for_each(v.begin(),v.end(),[](int val)
{ 
 cout << val;
});

很明顯,相比于傳統(tǒng)的for循環(huán)、函數(shù)指針和仿函數(shù),使用lambda函數(shù)更加簡潔。如果處理vector成員的業(yè)務(wù)代碼更加復(fù)雜,那么更能凸顯Lambda函數(shù)的便捷。而且這么寫之后執(zhí)行效率反而會提高,因為編譯器有可能使用循環(huán)展開來加速執(zhí)行過程。

以上就是一文讀懂c++11 Lambda表達(dá)式的詳細(xì)內(nèi)容,更多關(guān)于c++11 Lambda表達(dá)式的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論