??歡迎來到C++專欄~~ lambda 表達(dá)式
- (???(??? )??,我是Scort
- 目前狀態(tài):大三非科班啃C++中
- ??博客主頁:張小姐的貓~江湖背景
- 快上車??,握好方向盤跟我有一起打天下嘞!
- 送給自己的一句雞湯??:
- ??真正的大師永遠(yuǎn)懷著一顆學(xué)徒的心
- 作者水平很有限,如果發(fā)現(xiàn)錯(cuò)誤,可在評(píng)論區(qū)指正,感謝??
- ????歡迎持續(xù)關(guān)注!
![]()
一. 概念
自 C++11 開始,C++ 有三種方式可以像函數(shù)使用的對(duì)象 / 類型:
函數(shù)指針
仿函數(shù)
Lambda 表達(dá)式
此處要注意:greater后面的括號(hào)是什么時(shí)候要加上?(容易混淆)
- 首先我們要清楚,傳的是對(duì)象還是類型:傳的是對(duì)象就要加(),如果是類型就不用
言歸正傳,lambda
表達(dá)式本質(zhì)上就是一個(gè)匿名函數(shù)
這里舉個(gè)例子:
struct Items
{
string _name; //名字
double _price; //價(jià)格
int _num; //數(shù)量
};
如果要對(duì)若干對(duì)象分別按照價(jià)格和數(shù)量進(jìn)行升序、降序排序
可以使用 sort 函數(shù),但由于這里待排序的元素為自定義類,如果想按照我們的數(shù)據(jù)進(jìn)行排序,只能通過仿函數(shù)來實(shí)現(xiàn)了,那豈不是要實(shí)現(xiàn)6個(gè)仿函數(shù)?
有點(diǎn)麻煩了
struct ComparePriceLess//價(jià)格降序
{
bool operator()(const Goods& g1, const Goods& g2)
{
return g1._price < g2._price;
}
};
struct ComparePriceGreater//價(jià)格升序
{
bool operator()(const Goods& g1, const Goods& g2)
{
return g1._price > g2._price;
}
};
int main()
{
vector<Goods> v = { { "蘋果", 2.1, 300 }, { "香蕉", 3.3, 100 }, { "橙子", 2.2, 1000 }, { "菠蘿", 1.5, 1 } };
sort(v.begin(), v.end(), ComparePriceLess()); //價(jià)格升序
sort(v.begin(), v.end(), ComparePriceGreater()); //價(jià)格降序
return 0;
}
為此lambda
表達(dá)式就橫空出世了
二. 語法
lambda 表達(dá)式定義:
[capture-list] (parameters) mutable -> return-type { statement }
表達(dá)式各部分說明:
-
[capture-list]
: 捕捉列表,該列表總是出現(xiàn)在lambda函數(shù)的開始位置,編譯器根據(jù)[]來判斷接下來的代碼是否為lambda函數(shù),捕捉列表能夠捕捉上下文中的變量供lambda函數(shù)使用 -
(parameters)
:參數(shù)列表。與普通函數(shù)的參數(shù)列表一致,如果不需要參數(shù)傳遞,則可以連同()一起省略(無參可以省略) -
mutable
:默認(rèn)情況下,lambda函數(shù)總是一個(gè)const函數(shù),mutable可以取消其常量
性。使用該修飾符時(shí),參數(shù)列表不可省略(即使參數(shù)為空) -
->returntype
:返回值類型。用追蹤返回類型形式聲明函數(shù)的返回值類型,沒有返回值時(shí)此部分可省略。返回值類型明確情況下,也可省略,由編譯器對(duì)返回類型進(jìn)行推導(dǎo) -
{statement}
:函數(shù)體。在該函數(shù)體內(nèi),除了可以使用其參數(shù)外,還可以使用所有捕獲到的變量
除了捕獲列表,Lambda表達(dá)式的其它地方其實(shí)和普通的函數(shù)基本一樣,lambda 參數(shù)列表和返回值類型都是可有可無的,但捕捉列表和函數(shù)體是不可省略的,因此最簡(jiǎn)單的lambda函數(shù)如下:
int main()
{
[]{}; //最簡(jiǎn)單的lambda表達(dá)式
return 0;
}
再舉個(gè)函數(shù)相加的例子:
int main()
{
//兩個(gè)函數(shù)相加
auto add1 = [] (int a, int b)->int{return a + b; };
cout << add1(1, 2) << endl;
//省略返回值
auto add2 = [](int a, int b){return a + b; };
cout << add1(1, 2) << endl;
return 0;
}
如果我們要不傳參數(shù),該怎么樣實(shí)現(xiàn)呢?
三. 捕獲方式
Lambda 表達(dá)式最基本的兩種捕獲方式是:按值捕獲和按引用捕獲
-
[var]
:值傳遞捕捉變量var -
[=]
:值傳遞捕獲所有父作用域中的變量(成員函數(shù)包括this指針) -
[&var]
:引用傳遞捕捉變量var -
[&]
:引用傳遞捕捉所有父作用域中的變量(成員函數(shù)包括this指針)
注意:
- 1??父作用域要包含lambda函數(shù)語句(一般指的是當(dāng)前所在的函數(shù)(棧幀))
- 2??語法上捕捉可由多個(gè)捕捉項(xiàng)組成,并以逗號(hào)分割(就是可以混合著來)
//混合捕捉:a是引用捕捉,其他都是傳值捕捉
auto f1 = [=, &a]() {
cout << a << b << c << d << e << endl;
};
f1();
- 3??捕捉列表不允許變量重復(fù)傳遞,否則就會(huì)導(dǎo)致編譯錯(cuò)誤
- 4??在塊作用域以外的lambda函數(shù)捕捉列表必須為空
- 5??在塊作用域中的lambda函數(shù)僅能捕捉父作用域中局部變量,捕捉任何非此作用域或者非局部變量都 會(huì)導(dǎo)致編譯報(bào)錯(cuò)。
來個(gè)例題:此處的f
可以++嗎?
int f = 0;
int main()
{
int a, b, c, d, e;
a = b = c = d = e = 1;
//混合捕捉:a是引用捕捉,其他都是傳值捕捉
auto f1 = [=, &a]() {
f++;
cout << a << b << c << d << e << endl;
};
f1();
return 0;
}
f為什么可以呢?
- 因?yàn)?code>f是全局變量不存在于棧幀里,存在于靜態(tài)區(qū),哪個(gè)位置都可以用它!
復(fù)習(xí)一下:
對(duì)象的作用域和存儲(chǔ)區(qū)域要分清楚:
- 生命周期是和存儲(chǔ)的區(qū)域有關(guān)系
- 作用域(編譯器編譯,用的地方能否找到) 局部 —> 全局
??相互賦值
lambda表達(dá)式之間不能相互賦值,就算是兩個(gè)一模一樣的也不行
lambda 表達(dá)式會(huì)被處理為函數(shù)對(duì)象,該函數(shù)對(duì)象對(duì)應(yīng)的類名叫做<lambda_uuid>
類名中的uuid
叫做通用唯一識(shí)別碼,簡(jiǎn)單來說就是通過算法生成的一串字符串,它具有隨機(jī)性和不重復(fù)性,保證在當(dāng)前程序中每次生成不同的 uuid,因?yàn)?lambda 表達(dá)式底層的類名包含 uuid,這就保證了每個(gè) lambda 表達(dá)式底層類名都是唯一的!
void (*PF)();
int main()
{
auto f1 = []{cout << "hello world" << endl; };
auto f2 = []{cout << "hello world" << endl; };
f1 = f2; // 編譯失敗--->提示找不到operator=()
//實(shí)例化后的兩個(gè)lambda類型,類型不一樣
auto f3(f2);
f3();
// 可以將lambda表達(dá)式賦值給相同類型的函數(shù)指針
PF = f2;
PF();
return 0;
}
但是lambda表達(dá)式賦值給相同類型的函數(shù)指針
就是在我們看來是一樣的,但是其底層大有不同!
四. 底層實(shí)現(xiàn)
實(shí)際在底層編譯器對(duì)于lambda表達(dá)式的處理方式,完全就是按照函數(shù)對(duì)象的方式處理的,就是對(duì)()進(jìn)行了重載
class Add
{
public:
Add(int base)
:_base(base)
{}
int operator()(int num)
{
return _base + num;
}
private:
int _base;
};
int main()
{
int base = 1;
//函數(shù)對(duì)象
Add add1(base);
add1(1000);
//lambda表達(dá)式
auto add2 = [base](int num)->int
{
return base + num;
};
add2(1000);
return 0;
}
對(duì)反匯編進(jìn)行觀察:
當(dāng)創(chuàng)建add對(duì)象的時(shí)候是構(gòu)造函數(shù),使用add對(duì)象的時(shí)候就是會(huì)調(diào)用 Add 類的 () 運(yùn)算符重載函數(shù)
lambda 表達(dá)式同樣如此:會(huì)調(diào)用 <lambda_uuid>
類的構(gòu)造函數(shù),在使用add2對(duì)象時(shí),會(huì)調(diào)用<lambda_uuid>
類的 ()運(yùn)算符重載函數(shù)
其本質(zhì)就是:lambda表達(dá)式在底層被轉(zhuǎn)換成了仿函數(shù)
當(dāng)我們定義一個(gè)lambda表達(dá)式后,編譯器會(huì)自動(dòng)生成一個(gè)類,在該類中對(duì) () 運(yùn)算符進(jìn)行重載,實(shí)際 lambda 函數(shù)體的實(shí)現(xiàn)就是這個(gè)仿函數(shù) operator() 的實(shí)現(xiàn),在調(diào)用 lambda 表達(dá)式時(shí),參數(shù)列表和捕獲列表的參數(shù),最終都傳遞給了仿函數(shù)的 operator()
五. mutable(作用不大)
在實(shí)際使用中,比如實(shí)現(xiàn)一個(gè)交換函數(shù),我們用 lambda 表達(dá)式實(shí)現(xiàn):
int main()
{
auto swap2 = [x, y]()
{
int tmp = x;
x = y;
y = tmp;
};
swap2();
return 0;
}
這里我們發(fā)現(xiàn)是傳值傳參!果然編譯不通過,因?yàn)閭髦挡东@到的變量默認(rèn)是不可修改的(const):
如果要取消其常量屬性,就需要在 lambda 表達(dá)式中加上 mutable
像這樣:
auto swap2 = [x, y]() mutable //改變的是形參,實(shí)參無影響,所以沒用
{
int tmp = x;
x = y;
y = tmp;
};
但是捕捉列表是傳值捕捉過來的,不影響外面的實(shí)參;所以這種方法無法完成交換功能
文章來源:http://www.zghlxwxcb.cn/news/detail-809187.html
??寫在最后
文章來源地址http://www.zghlxwxcb.cn/news/detail-809187.html
到了這里,關(guān)于【C++】C++11語法 ~ lambda 表達(dá)式的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!