一文詳解C++11中的lambda函數(shù)
我可以明確告訴你:lambda函數(shù)是C++11中最重要的,使用最廣泛的,最具現(xiàn)代風(fēng)格的內(nèi)容,lambda函數(shù)的出現(xiàn)改變了C++編程的思維方式。
#include<iostream>
using namespace std;
int main()
{
int girls=3,boys=4;
auto totalChild=[](int x,int y)->int{return x+y;};
return totalChild(girls,boys);
}
上面中,auto和lambda函數(shù)的配合是一種經(jīng)典生成局部函數(shù)的方法。lambda函數(shù)和普通函數(shù)最大的不同的地方就是它沒(méi)有名字,所以 lambda函數(shù)是右值。
lambda函數(shù)相較于,函數(shù)指針,仿函數(shù),它不僅簡(jiǎn)單,而且效率高。
1.lambda函數(shù)語(yǔ)法
[capture](parameters)mutable ->return-type{statement}
- [capture]:捕捉列表。捕獲父作用域中可用的變量,供lambda函數(shù)使用,[]是lambda函數(shù)的導(dǎo)出符號(hào)。
- (parameters):參數(shù)列表。和普通函數(shù)的參數(shù)列表一樣,如果沒(méi)有參數(shù)可以省略
- mutable:修飾符號(hào)。lambda函數(shù)默認(rèn)是一個(gè)const修飾的函數(shù),mutab可以取消其常量性
- ->return-type:返回類(lèi)型。類(lèi)似于返回值后置的語(yǔ)法,如果沒(méi)有返回值或者返回類(lèi)型可自動(dòng)推斷就可省略聲明返回類(lèi)型。
- {statement}:函數(shù)體。
根據(jù)上面語(yǔ)法,我們知道[]{}是一種最簡(jiǎn)單的lambda函數(shù),而且我們發(fā)現(xiàn),從語(yǔ)法角度來(lái)看,lambda函數(shù)比普通函數(shù)多了一個(gè)捕獲列表,這是它的精髓所在。
1.1 捕獲列表
int main()
{
[]{};//最簡(jiǎn)單的lambda函數(shù)
int a=3;
int b=4;
[=]{return a+b;};
auto func1=[&](int c){b=a+c;};
auto func2=[=,&b](int c)->int{return b+=a+c;};
}。上面代碼中,我們可以使用捕獲列表來(lái)捕獲,變量a和b
被捕獲的變量和基于參數(shù)傳遞的變量是不同的,被捕獲的變量更是一種lambda函數(shù)的初始狀態(tài)。
捕獲列表是由多個(gè)捕獲項(xiàng)組成的:
- [var]:表示按值方式捕獲變量var
- [=]:表示按值捕獲其父作用域中所有可用的變量(包括this)
- [&var]:表示按引用捕獲變量var
- [&]:表示按引用捕獲其父作用域中所有可用的變量(包括this)
- [this]:表示按值傳遞當(dāng)前的this指針
特別的這些捕獲項(xiàng)可用組合使用,例如:[=,&a,&b]表示以引用捕獲a和b,按值捕獲其他所有變量。[&,a,this]表示按值捕獲a和this,按引用捕獲其他所有變量。
下面看一段代碼
#include<iostream>
using namespace std;
int main()
{
int j=12;
auto fun1=[=]{return j;};
auto fun2=[&]{return j;};
cout<<"fun1: "<<fun1()<<endl;
cout<<"fun2: "<<fun2()<<endl;
j++;
cout<<"fun1: "<<fun1()<<endl;
cout<<"fun2: "<<fun2()<<endl;
}
/*
fun1: 12
fun2: 12
fun1: 12
fun2: 13
*/
當(dāng)j++后,再次調(diào)用func1()時(shí),我們發(fā)現(xiàn)它里面的j卻保持不變,所以上面這段代碼反應(yīng)了一個(gè)事實(shí):捕獲的變量是lambda函數(shù)的初始狀態(tài)。
在使用lambda函數(shù)時(shí),按值傳遞的變量成為函數(shù)中的常量,它不會(huì)再運(yùn)行過(guò)程中改變,按引用傳遞的變量,它類(lèi)似于函數(shù)參數(shù),他會(huì)隨時(shí)檢查其值。
#include<iostream>
using namespace std;
int temp=0;
int main()
{
static int a=0;
auto fun=[]{temp++;a++;};
fun();
fun();
cout<<temp<<endl;//2
cout<<a<<endl;//2;
[temp]{};//編譯錯(cuò)誤
[a]{};//編譯錯(cuò)誤
}
lambda函數(shù)中也可以直接使用全局變量,但是如果fun1這樣就是錯(cuò)誤的,因?yàn)?lambda函數(shù)只能捕獲其父作用域中可用的自動(dòng)變量,而靜態(tài)變量不需要捕獲,可以直接使用。
#include<iostream>
using namespace std;
int main()
{
int a=1;
cout<<"a="<<a<<endl;
auto foo1=[&]()
{
a++;
cout<<"a="<<a<<endl;
auto foo2=[&]()
{
a++;
cout<<"a="<<a<<endl;
};
foo2();
};
foo1();
}
/*
a=1
a=2
a=3
*/上面代碼中說(shuō)明,lambda函數(shù)中還可以使用lambda函數(shù),這樣子,上面代碼就狠像pascal語(yǔ)言中的內(nèi)嵌函數(shù)。
1.2 mutable修飾符
lambda函數(shù)是具有常量性的,下面這段代碼是在stackoverflow網(wǎng)站中的一次討論:
int main()
{
int val;
auto fun1=[=]{val=3;};//編譯失敗,val無(wú)法被賦值
auto fun2=[=]() mutable {val=3;};
auto fun3=[&]{val=3;};
auto fun4=[](int v){v=3;};
fun4(val);
}
上面中,fun1無(wú)法通過(guò)編譯,因?yàn)関al是按值傳遞的,所以在函數(shù)體中,val就是一個(gè)只讀常量,無(wú)法對(duì)其進(jìn)行賦值,我們可以使用修飾符mutable來(lái)取消其只讀屬性。這樣的目的是只是提供一種語(yǔ)法上的可能,在實(shí)際使用的時(shí)候,我們一般不需要使用mutable,如果需要修改按值傳遞的值,我們可以直接按值傳遞參數(shù),而不是捕獲它。
實(shí)際上,lambda函數(shù)中的捕獲變量,更像是函數(shù)對(duì)象(仿函數(shù))中的私有數(shù)據(jù)成員:
class fun1
{
private:
int val;
public:
fun1(int v):val(v){};
void operator()const{val=3;};//編譯出錯(cuò)
}
默認(rèn)情況下,按值捕獲的變量,如果不加mutable,它就會(huì)等價(jià)于上面的仿函數(shù),這里的operator()就是const修飾的,它不允許修改val。
1.3 匿名lambda函數(shù)
lambda函數(shù)本身是右值,它沒(méi)有名字,它本身就是匿名的,我們一般通過(guò)auto來(lái)賦予它一個(gè)名字,這樣就能生成類(lèi)似一個(gè)局部函數(shù)的效果。我們也可以不使用auto,我們可以直接生成一個(gè)lambda函數(shù),然后調(diào)用,例如下面這段代碼:
#include<iostream>
using namespace std;
int main()
{
const int a=[]{
int ret=0;
for(int i=0;i<100;i++)
{
ret+=i;
}
return ret;
}();
cout<<a<<endl;
}
lambda函數(shù)定義后直接調(diào)用。
實(shí)際上,lambda函數(shù)的設(shè)計(jì)初衷就是:就地書(shū)寫(xiě),就地使用。所以諸如上面的寫(xiě)法非常常見(jiàn)。
2.lambda與STL
lambda函數(shù)的出現(xiàn),讓我們發(fā)現(xiàn)使用STL算法更加簡(jiǎn)單了。
例如for_each()算法,它接收3個(gè)參數(shù),前兩個(gè)是指示范圍的迭代器類(lèi)型,第3個(gè)是接收一個(gè)參數(shù)的函數(shù)符(即仿函數(shù),函數(shù)指針,lambda函數(shù))。
#include<vector>
#include<algorithm>
#include<iostream>
using namespace std;
extern vector<int> nums;
void OneCond(int val)
{
//傳統(tǒng)for方法
for(auto i=nums.begin();i!=nums.end();++i)
{
if(*i==val)
break;
}
//使用adapter
find_if(nums.begin(),nums.end(),bind2nd(equal_to<int>(),val));
//使用lambda函數(shù)
find_if(nums.begin(),nums.end(),[=](int i){
return i==val;
});
}
上面代碼中,有些人認(rèn)為使用這種adapter會(huì)簡(jiǎn)單一點(diǎn),就像這里的equal_to<int>()它就是是一個(gè)函數(shù)對(duì)象,我只能說(shuō)仁者見(jiàn)仁。但是這種使用adapter的方式創(chuàng)建函數(shù)對(duì)象,要求程序員懂很多STL的知識(shí),而且可讀性不好,例如下面這段代碼
#include<vector>
#include<algorithm>
#include<iostream>
using namespace std;
extern vector<int> nums;
void twoCond(int low,int high)
{
for(auto i=nums.begin();i!=nums.end();i++)
{
if(*i>=low && *i<high)break;
}
find_if(nums.begin(),nums.end(),compose2(
logical_and<bool>(),
bind2nd(less<int>(),high),
bind2nd(greater_equal<int>(),low)
));
find_if(nums.begin(),nums.end(),[=](int i)
{
return i>=low && i<high;
});
}
再看看下面的lambda簡(jiǎn)化STL的例子
#include<vector>
#include<algorithm>
#include<iostream>
using namespace std;
vector<int> nums;
void Add(const int val)
{
auto print =[]{
for(auto s:nums)
{
cout<<s<<"\t";
}
cout<<endl;
};
for(auto i=nums.begin();i!=nums.end();i++)
{
*i=*i+val;
}
print();
for_each(nums.begin(),nums.end(),bind2nd(plus<int>(),val));
print();
transform(nums.begin(),nums.end(),nums.begin(),bind2nd(plus<int>(),val));
print();
for_each(nums.begin(),nums.end(),[=](int &i){i+=val;});
print();
}
int main()
{
for(int i=0;i<10;i++)
nums.emplace_back(i);
Add(10);
}
/*
10 11 12 13 14 15 16 17 18 19
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
30 31 32 33 34 35 36 37 38 39
*/
我們發(fā)現(xiàn)上面代碼運(yùn)行后,第二行相較于第一行沒(méi)有變化,如果熟悉STL,你會(huì)狠容易發(fā)現(xiàn),因?yàn)閒or_each()它不會(huì)寫(xiě)回,而transform它會(huì)寫(xiě)回。STL新手就會(huì)容易犯這個(gè)錯(cuò)誤,如果你使用lambda函數(shù)這些東西就不需要了。
#include<vector>
#include<algorithm>
#include<iostream>
#include<numeric>
using namespace std;
void Stat(vector<int> &v)
{
int errors;
int score;
auto print =[&]{
cout<<"Errors: "<<errors<<endl
<<"Score: "<<score<<endl;
};
errors=accumulate(v.begin(),v.end(),0);
score=accumulate(v.begin(),v.end(),100,minus<int>());
print();
errors=0;
score=100;
for_each(v.begin(),v.end(),[&](int i){
errors+=i;
score-=i;
});
print();
}
int main()
{
vector<int> v(10);
generate(v.begin(),v.end(),[]{return rand()&10;});
Stat(v);
}
總之,lambda函數(shù)非常好用
以上就是一文詳解C++11中的lambda函數(shù)的詳細(xì)內(nèi)容,更多關(guān)于C++11 lambda函數(shù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
c語(yǔ)言實(shí)現(xiàn)從源文件從文本到可執(zhí)行文件經(jīng)歷的過(guò)程
這篇文章主要介紹了c語(yǔ)言實(shí)現(xiàn)從源文件從文本到可執(zhí)行文件經(jīng)歷的過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
基于C語(yǔ)言實(shí)現(xiàn)計(jì)算生辰八字五行的示例詳解
生辰八字,簡(jiǎn)稱(chēng)八字,是指一個(gè)人出生時(shí)的干支歷日期;年月日時(shí)共四柱干支,每柱兩字,合共八個(gè)字。這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)計(jì)算生辰八字五行的示例代碼,需要的可以參考一下2023-03-03
dev-c++創(chuàng)建lib(靜態(tài)鏈接庫(kù))文件的實(shí)現(xiàn)步驟
本文主要介紹了dev-c++創(chuàng)建lib(靜態(tài)鏈接庫(kù))文件的實(shí)現(xiàn)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
Opencv3.4.0實(shí)現(xiàn)視頻中的幀保存為圖片功能
這篇文章主要為大家詳細(xì)介紹了Opencv3.4.0實(shí)現(xiàn)視頻中的幀保存為圖片功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05
C語(yǔ)言實(shí)現(xiàn)三子棋(井字棋)算法
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)三子棋(井字棋)算法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07
詳解VS2010實(shí)現(xiàn)創(chuàng)建并生成動(dòng)態(tài)鏈接庫(kù)dll的方法
在某些應(yīng)用程序場(chǎng)景下,需要將一些類(lèi)或者方法編譯成動(dòng)態(tài)鏈接庫(kù)dll,以便別的.exe或者.dll文件可以通過(guò)第三方庫(kù)的方式進(jìn)行調(diào)用,下面就簡(jiǎn)單介紹一下如何通過(guò)VS2010來(lái)創(chuàng)建動(dòng)態(tài)鏈接庫(kù)2022-12-12

