網(wǎng)站首頁 編程語言 正文
我可以明確告訴你: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ù)最大的不同的地方就是它沒有名字,所以 lambda函數(shù)是右值。
lambda函數(shù)相較于,函數(shù)指針,仿函數(shù),它不僅簡單,而且效率高。
1.lambda函數(shù)語法
[capture](parameters)mutable ->return-type{statement}
- [capture]:捕捉列表。捕獲父作用域中可用的變量,供lambda函數(shù)使用,[]是lambda函數(shù)的導(dǎo)出符號。
- (parameters):參數(shù)列表。和普通函數(shù)的參數(shù)列表一樣,如果沒有參數(shù)可以省略
- mutable:修飾符號。lambda函數(shù)默認(rèn)是一個const修飾的函數(shù),mutab可以取消其常量性
- ->return-type:返回類型。類似于返回值后置的語法,如果沒有返回值或者返回類型可自動推斷就可省略聲明返回類型。
- {statement}:函數(shù)體。
根據(jù)上面語法,我們知道[]{}是一種最簡單的lambda函數(shù),而且我們發(fā)現(xiàn),從語法角度來看,lambda函數(shù)比普通函數(shù)多了一個捕獲列表,這是它的精髓所在。
1.1 捕獲列表
int main() { []{};//最簡單的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;}; }。
上面代碼中,我們可以使用捕獲列表來捕獲,變量a和b
被捕獲的變量和基于參數(shù)傳遞的變量是不同的,被捕獲的變量更是一種lambda函數(shù)的初始狀態(tài)。
捕獲列表是由多個捕獲項(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()時,我們發(fā)現(xiàn)它里面的j卻保持不變,所以上面這段代碼反應(yīng)了一個事實(shí):捕獲的變量是lambda函數(shù)的初始狀態(tài)。
在使用lambda函數(shù)時,按值傳遞的變量成為函數(shù)中的常量,它不會再運(yùn)行過程中改變,按引用傳遞的變量,它類似于函數(shù)參數(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]{};//編譯錯誤 [a]{};//編譯錯誤 }
lambda函數(shù)中也可以直接使用全局變量,但是如果fun1這樣就是錯誤的,因?yàn)?lambda函數(shù)只能捕獲其父作用域中可用的自動變量,而靜態(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 */
上面代碼中說明,lambda函數(shù)中還可以使用lambda函數(shù),這樣子,上面代碼就狠像pascal語言中的內(nèi)嵌函數(shù)。
1.2 mutable修飾符
lambda函數(shù)是具有常量性的,下面這段代碼是在stackoverflow網(wǎng)站中的一次討論:
int main() { int val; auto fun1=[=]{val=3;};//編譯失敗,val無法被賦值 auto fun2=[=]() mutable {val=3;}; auto fun3=[&]{val=3;}; auto fun4=[](int v){v=3;}; fun4(val); }
上面中,fun1無法通過編譯,因?yàn)関al是按值傳遞的,所以在函數(shù)體中,val就是一個只讀常量,無法對其進(jìn)行賦值,我們可以使用修飾符mutable來取消其只讀屬性。這樣的目的是只是提供一種語法上的可能,在實(shí)際使用的時候,我們一般不需要使用mutable,如果需要修改按值傳遞的值,我們可以直接按值傳遞參數(shù),而不是捕獲它。
實(shí)際上,lambda函數(shù)中的捕獲變量,更像是函數(shù)對象(仿函數(shù))中的私有數(shù)據(jù)成員:
class fun1 { private: int val; public: fun1(int v):val(v){}; void operator()const{val=3;};//編譯出錯 }
默認(rèn)情況下,按值捕獲的變量,如果不加mutable,它就會等價于上面的仿函數(shù),這里的operator()就是const修飾的,它不允許修改val。
1.3 匿名lambda函數(shù)
lambda函數(shù)本身是右值,它沒有名字,它本身就是匿名的,我們一般通過auto來賦予它一個名字,這樣就能生成類似一個局部函數(shù)的效果。我們也可以不使用auto,我們可以直接生成一個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è)計初衷就是:就地書寫,就地使用。所以諸如上面的寫法非常常見。
2.lambda與STL
lambda函數(shù)的出現(xiàn),讓我們發(fā)現(xiàn)使用STL算法更加簡單了。
例如for_each()算法,它接收3個參數(shù),前兩個是指示范圍的迭代器類型,第3個是接收一個參數(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會簡單一點(diǎn),就像這里的equal_to<int>()它就是是一個函數(shù)對象,我只能說仁者見仁。但是這種使用adapter的方式創(chuàng)建函數(shù)對象,要求程序員懂很多STL的知識,而且可讀性不好,例如下面這段代碼
#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簡化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)行后,第二行相較于第一行沒有變化,如果熟悉STL,你會狠容易發(fā)現(xiàn),因?yàn)閒or_each()它不會寫回,而transform它會寫回。STL新手就會容易犯這個錯誤,如果你使用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ù)非常好用
原文鏈接:https://blog.csdn.net/m0_71009069/article/details/128881416
相關(guān)推薦
- 2022-05-28 解決tomcat啟動?ssm項(xiàng)目出現(xiàn)亂碼的問題_Tomcat
- 2023-05-24 Golang?HTTP編程的源碼解析詳解_Golang
- 2022-09-19 Python正則表達(dá)式以及常用匹配實(shí)例_python
- 2022-06-01 Python?如何將?matplotlib?圖表集成進(jìn)到PDF?中_python
- 2024-01-14 在springboot中給mybatis加攔截器
- 2022-08-10 Go語言pointer及switch?fallthrough實(shí)戰(zhàn)詳解_Golang
- 2022-10-07 android?studio廣播機(jī)制使用詳解_Android
- 2022-03-31 C#循環(huán)與循環(huán)控制的表達(dá)式樹實(shí)現(xiàn)_C#教程
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支