網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
前言
Lambda(匿名函數(shù))表達(dá)式是C++11最重要的特性之一,lambda來(lái)源于函數(shù)式編程的概念,也是現(xiàn)代編程語(yǔ)言的一個(gè)特點(diǎn)。
優(yōu)點(diǎn)如下:
- 聲明式編程風(fēng)格:就地匿名定義目標(biāo)函數(shù)或函數(shù)對(duì)象,有更好的可讀性和可維護(hù)性。
- 簡(jiǎn)潔:不需要額外寫(xiě)一個(gè)命名函數(shù)或函數(shù)對(duì)象,,避免了代碼膨脹和功能分散。
- 更加靈活:在需要的時(shí)間和地點(diǎn)實(shí)現(xiàn)功能閉包。
概念及基本用法
lambda表達(dá)式定義了一個(gè)匿名函數(shù),并且可以捕獲一定范圍內(nèi)的變量。語(yǔ)法形式如下:
[ capture ] ( params ) opt -> ret { body; };
- capture:捕獲列表
- params:參數(shù)列表
- opt:函數(shù)選項(xiàng)
- ret:返回值類型
- body:函數(shù)體
一個(gè)完整的lambda表達(dá)式是這樣:
auto f = [](int a) -> int {return a + 1;};
cout << f(3) << endl; //輸出4
以上定義了一個(gè)完整的lambda,但是在實(shí)際的使用中,可以省略其返回值的定義,編譯器會(huì)根據(jù)return語(yǔ)句進(jìn)行自動(dòng)推導(dǎo)返回值類型。
省略過(guò)后如下:
auto f = [](int a) {return a + 1;};
需要注意的是,初始化列表不能用于返回值的自動(dòng)推導(dǎo):
如:auto f = [](){return {1,2};}; //error:無(wú)法推導(dǎo)返回值類型
另外,如果表達(dá)式?jīng)]有參數(shù)列表時(shí),也可以省略,如:
auto f = []{return 1;};
捕獲變量
lambda表達(dá)式可以通過(guò)捕獲列表捕獲一定范圍內(nèi)的變量,主要有以下幾種情況:
- [] 不捕獲任何變量
- [&]捕獲外部作用域中所有變量,并作為引用在函數(shù)體中使用(按引用捕獲)
- [=]捕獲外部作用域中所有變量,并作為副本在函數(shù)體重使用(按值捕獲)
- [=,&foo] 按值捕獲外部作用域中所有變量,并按引用捕獲foo變量
- [bar] 按值捕獲bar變量,同時(shí)不捕獲其他變量
- [this] 捕獲當(dāng)前類中的this指針,讓表達(dá)式擁有和當(dāng)前類成員函數(shù)同樣的訪問(wèn)權(quán)限。如果已經(jīng)使用了&或者=,就默認(rèn)添加此選項(xiàng)。捕獲this的目的是可以在lambda中使用當(dāng)前類的成員變量和成員函數(shù)。
通過(guò)示例來(lái)看具體用法:
class A
{
public:
int i_ = 0;
void func(int x,int y)
{
auto x1 = []{return i_;}; // error,沒(méi)有捕獲外部變量
auto x2 = [=]{return i_ + x + y;}; //ok,按值捕獲所有外部變量
auto x3 = [&]{return i_ + x + y;}; //ok,按引用捕獲所有外部變量
auto x4 = [this]{return i_;}; //ok,捕獲this指針
auto x5 = [this]{return i_ + x + y;}; //error,沒(méi)有捕獲x和y變量
auto x6 = [this,x,y]{return i_ + x + y;}; //ok,捕獲了this指針和x、y變量
auto x7 = [this]{return i_++;}; //ok,捕獲了this指針,修改成員變量的值
}
};
int a = 0 , b = 0 ;
auto f1 = []{return a;}; // error,沒(méi)有捕獲外部變量
auto f2 = [&]{return a++;}; //ok,捕獲所有外部變量,并對(duì)a變量自加
auto f3 = [=]{return a;}; //ok,捕獲所有外部變量,并返回a
auto f4 = [=]{return a++;}; //error,a變量是以復(fù)制方式捕獲的,不能修改
auto f5 = [a]{return a+b;}; //error,沒(méi)有捕獲b變量
auto f6 = [a,&b]{return a+ (b++);}; //ok,捕獲a以及b的引用,對(duì)b進(jìn)行自加
auto f7 = [=,&b]{return a+ (b++);}; //ok, 捕獲所有外部變量和b的引用,對(duì)b進(jìn)行自加
需要注意的是,lambda無(wú)法修改按值捕獲的外部變量,如果需要修改外部變量,可以通過(guò)引用方式捕獲。
關(guān)于lambda表達(dá)式的延遲調(diào)用很容易出錯(cuò),如下:
int a = 0;
auto f = [=]{return a;};
a += 1;
cout << f() << endl;
以上示例中,lambda按值捕獲了所有外部變量,在捕獲的時(shí)候 a的值就已經(jīng)被復(fù)制到 f 中了,之后a被修改,但是f里面存儲(chǔ)的a仍然是捕獲時(shí)的值,所以最終輸出的是 0.
如果希望lambda表達(dá)式在調(diào)用的時(shí)候能夠訪問(wèn)外部變量,需要使用引用方式捕獲。
所以簡(jiǎn)單來(lái)說(shuō),按值捕獲,外部變量會(huì)被復(fù)制一份存儲(chǔ)在lambda表達(dá)式變量中。
如果是按值捕獲并且又想修改外部變量,可以顯示指明lambda表達(dá)式為mutable:
int a = 0;
auto f1 = [=]{return a++;}; //error,修改按值捕獲的外部變量
auto f2 = [=]() mutable {return a++;}; //ok
被mutable修飾的lambda表達(dá)式就算沒(méi)有參數(shù)也要寫(xiě)明參數(shù)列表。
lambda表達(dá)式類型
lambda表達(dá)式的類型在C++11中被稱為“閉包類型”,它是一個(gè)特殊的,匿名的非nunion的類型。
可以認(rèn)為它是帶有一個(gè)operator()的類,即仿函數(shù)。
我們可以通過(guò)std::function和std::bind來(lái)存儲(chǔ)和操作lambda表達(dá)式:
std::function<int(int)> f1 = [](int a){return a;};
std::function<int(void)> f2 = std::bind([](int a){return a;},123);
另外,對(duì)于沒(méi)有捕獲任何變量的lambda表達(dá)式,還可以被轉(zhuǎn)換成一個(gè)普通的函數(shù)指針:
using func_t = int(*)(int);
func_t f = [](int a){return a;};
f(123);
lambda可以說(shuō)是就地定義仿函數(shù)閉包的“語(yǔ)法 糖”。它的捕獲列表捕獲住任何外部變量,最終都會(huì)變?yōu)殚]包類型的成員變量。而一個(gè)成員變量的類的operator(),如果能直接被轉(zhuǎn)換為普通的函數(shù)指針,那么lambda表達(dá)式本身的this指針就丟掉了。而沒(méi)有捕獲任何外部變量的lambda表達(dá)式則不存在這個(gè)問(wèn)題。
需要注意的是,沒(méi)有捕獲變量的lambda表達(dá)式可以直接轉(zhuǎn)換為函數(shù)指針,而捕獲變量的lambda表達(dá)式則不能轉(zhuǎn)換為函數(shù)指針。如下:
typedef void(*Ptr)(int*);
Ptr p = [](int *p){delete p;}; //ok
Ptr p1 = [&](int *p){delete p;}; //error
前面說(shuō)到的按值捕獲無(wú)法修改捕獲的外部變量,因?yàn)榘凑誄++標(biāo)準(zhǔn),lambda表達(dá)式的operator()默認(rèn)是const的,一個(gè)const成員函數(shù)是無(wú)法修改成員變量的值,而mutable的作用,就是取消operator()的const限制。
聲明式的編程風(fēng)格
通過(guò)示例來(lái)看一下lambda的使用,在C++11之前,如果要用for_each函數(shù)將數(shù)組中的偶數(shù)數(shù)量打印出來(lái),代碼如下:
#include
#include
class Count
{
public:
Count(int &val):num(val){}
void operator()(int val){
if(!(val & 1)){
++num;
}
}
private:
int #
};
int main()
{
std::vector<int> v = {1,2,3,4,5,6,7};
int count = 0;
for_each(v.begin(),v.end(),Count(count));
std::cout << count << endl;
return 0;
}
如果使用lambda表達(dá)式,就可以簡(jiǎn)化一下,真正使用閉包概念來(lái)替換這里的仿函數(shù)。
#include
#include
int main()
{
std::vector<int> v = {1,2,3,4,5,6,7};
int count = 0;
for_each(v.begin(),v.end(),[&count](int val){
if(!(val & 1)){
++count;
}
});
std::cout << count << endl;
return 0;
}
lambda表達(dá)式的價(jià)值在于,就地封裝短小的功能閉包,方便地表達(dá)出我們希望執(zhí)行的具體操作,并讓上下文結(jié)合的更加緊,代碼更加簡(jiǎn)潔,更靈活,也提高了開(kāi)發(fā)效率及可維護(hù)性。
參考:《深入應(yīng)用C++11》
原文鏈接:https://luoyayun361.blog.csdn.net/article/details/123491439
相關(guān)推薦
- 2022-05-07 Python關(guān)鍵字之global與nonlocal_python
- 2022-02-15 小程序搜索框歷史記錄,去除重復(fù)搜索內(nèi)容,限制顯示條數(shù)
- 2022-05-22 分享幾種python?變量合并方法_python
- 2022-05-11 RestTemplate的post使用,token調(diào)用
- 2022-02-18 Redis - Redis command timed out nested exception i
- 2022-07-02 使用Element Plus <script lang=“ts“ setup> 加上lang=“ts
- 2022-06-14 C語(yǔ)言選擇、循環(huán)、函數(shù)、數(shù)組與操作符_C 語(yǔ)言
- 2022-07-26 ubuntu18.04+cuda10.2+tensorrt8.4.1.5配置安裝
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- 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)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤: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)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支