一、Lambda表達(dá)式引入
我們之前都是通過函數(shù)指針、仿函數(shù)的方式可以像函數(shù)使用的對象,在C++11之后,就有了Lambda表達(dá)式
struct Goods
{
string _name;
double _price;
};
struct Compare
{
bool operator()(const Goods& gl, const Goods& gr)
{
return gl._price <= gr._price;
}
};
int main()
{
Goods gds[] = { { "西瓜", 1.9 }, { "香蕉", 3.0 }, { "蘋果", 2.2 }, {"哈密瓜", 1.5} };
sort(gds, gds+sizeof(gds) / sizeof(gds[0]), Compare());
return 0;
}
為了實(shí)現(xiàn)一個(gè)比較算法, 都要重新去寫一個(gè)類,如果每次比較的邏輯不一樣,還要去實(shí)現(xiàn)多個(gè)類,特別是相同類的命名,看代碼的人就遭殃了,非常的煩,這些都非常地不方便。所以,C++11中的語法Lambda表達(dá)式由此登場。
下面我們來簡單看一下lambda表達(dá)式的簡單應(yīng)用:
#include <algorithm>
#include <vector>
#include <functional>
struct Goods
{
string _name;
double _price;
int _evaluate;
Goods(const char* str, double price, int evaluate)
:_name(str)
, _price(price)
, _evaluate(evaluate)
{}
};
int main()
{
vector<Goods> v = { { "蘋果", 2.1, 5 }, { "香蕉", 3, 4 },
{ "橙子", 2.2, 3 }, { "菠蘿", 1.5, 4 } };
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)
{ return g1._price < g2._price; });//小于是升序
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)
{ return g1._price > g2._price; });//大于時(shí)降序
return 0;
}
此時(shí),每次調(diào)用sort函數(shù)我們只需要傳入一個(gè)lambda表達(dá)式即可指明比較方式,大大提高了代碼的可閱讀性。下面,我們進(jìn)入的環(huán)節(jié)是Lambda表達(dá)式的語法。
二、Lambda表達(dá)式語法
上面的代碼就是用Lambda表達(dá)式解決的,我們清楚地可以看出Lambda表達(dá)式底層是匿名函數(shù)
。
Lambda表達(dá)式的格式:
[capture-list] (parameters) mutable -> return-type { statement }
Lambda表達(dá)式格式各項(xiàng)說明:
[capture-list] :
捕捉列表
(必寫項(xiàng)),該列表總是出現(xiàn)在lambda函數(shù)的開始位置,編譯器根據(jù)**[]來判斷接下來的代碼是否為lambda函數(shù),捕捉列表能夠捕捉上下文中的變量供lambda**函數(shù)使用。
(parameters):參數(shù)列表
(非必寫項(xiàng))。與普通函數(shù)的參數(shù)列表一致,如果不需要參數(shù)傳遞,則可以連同()一起省略
mutable:默認(rèn)情況下,lambda函數(shù)總是一個(gè)const函數(shù),mutable可以取消其常量性。使用該修飾符
時(shí),參數(shù)列表不可省略(即使參數(shù)為空)。一般不需要
->returntype:返回值類型
(非必寫項(xiàng))。用追蹤返回類型形式聲明函數(shù)的返回值類型,沒有返回值時(shí)此部分可省略。返回值類型明確情況下,也可省略,由編譯器對返回類型進(jìn)行推導(dǎo)
{statement}:函數(shù)體
。在該函數(shù)體內(nèi),除了可以使用其參數(shù)外,還可以使用所有捕獲到的變量。
除了捕捉列表外,Lambda表達(dá)式其他的地方和普通函數(shù)大致相同,Lambda表達(dá)式的參數(shù)列表和返回值可有可無,但是捕捉列表和函數(shù)體是必須寫,不可省略的,所以最簡單的Lambda表達(dá)式如下:
int main()
{
[]{};
return 0;
}
在舉個(gè)例子:兩個(gè)int對象相加的Lambda表達(dá)式:
int main()
{
//進(jìn)行int對象相加的Lambda表達(dá)式
//Lambda——可調(diào)用對象
//[](int x, int y)->bool {return x + y; };
auto compare = [](int x, int y) {return x + y; };
cout << compare(1, 2) << endl;
return 0;
}
Lambda表達(dá)式中的捕捉列表捕捉上下文中的編列可以被lambda使用,以及可以設(shè)置使用的方式是傳值還是傳引用:
[var]
:表示值傳遞捕捉變量var
[=]
:表示值傳遞方式捕捉所有父作用域中的變量,lambda上面的變量(父作用域是指包含lambda函數(shù)的語句塊)
[&var]
:表示引用傳遞捕捉變量var
[&]
:表示引用傳遞捕捉所有父作用域中的變量
[this]
:表示值傳遞捕捉當(dāng)前的this指針
注意:
捕捉列表可有多個(gè)捕捉項(xiàng)構(gòu)成,可以混合捕捉,以逗號分割。如[=,&a,b]
捕捉列表不允許變量重復(fù)傳遞:如[=,a]重復(fù)傳遞了變量a
三、Lambda表達(dá)式交換兩個(gè)值
很直觀的,我們會(huì)寫出下面的代碼:以傳值方式捕捉:
int main()
{
int a = 10, b = 20;
auto swap1 = [a,b]()
{
int tmp = a;
a = b;
b = tmp;
};
swap1();
cout << a << endl;
cout << b << endl;
}
捕捉列表的捕捉默認(rèn)的是傳值捕捉,但是上面的代碼存在問題:編譯根本就通不過,因?yàn)閭髦挡蹲降淖兞渴遣豢尚薷牡?,如果要取消常性,那么我們就要加上mutable:
int main()
{
int a = 10, b = 20;
auto swap1 = [a,b]()mutable
{
int tmp = a;
a = b;
b = tmp;
};
swap1();
cout << a << endl;
cout << b << endl;
}
由于是傳值捕捉,lambda函數(shù)中對a和b的修改不會(huì)影響外面的a、b變量,這與函數(shù)的傳值傳參是一個(gè)道理,所以這種方式并不能交換兩個(gè)值。所以實(shí)際上mutable用處不大。
正確的做法1:利用捕捉列表以引用傳參的方式交換兩個(gè)值:
int main()
{
int a = 10, b = 20;
auto swap1 = [&a,&b]()
{
int tmp = a;
a = b;
b = tmp;
};
swap1();
cout << a << endl;
cout << b << endl;
}
正確的做法2:利用參數(shù)列表以引用參數(shù)的方式傳遞:
int main()
{
int a = 10, b = 20;
auto swap1 = [](int&a,int&b)
{
int tmp = a;
a = b;
b = tmp;
};
swap1(a,b);
cout << a << endl;
cout << b << endl;
}
注意:Lambda表達(dá)式是一個(gè)匿名函數(shù),無法直接調(diào)用,可以利用auto將其值賦給一個(gè)變量,這時(shí)候這個(gè)變量就可以想函數(shù)一樣使用了
Lambda表達(dá)式如果比較長可以進(jìn)行換行寫,同時(shí)要注意函數(shù)體最后還有個(gè)分號哦。
四、Lambda表達(dá)式底層原理
- Lambda表達(dá)式的底層原理
實(shí)際就是編譯器在底層對于Lambda表達(dá)式的處理方式,完全就是按照函數(shù)對象的方式處理的,就是對()進(jìn)行了重載:
class Rate
{
private:
public:
Rate(double rate)
:_rate(rate)
{}
double operator()(double money, int year)
{
return money * _rate * year;
}
double _rate;
};
int main()
{
double rate = 0.49;
Rate r1(rate);
r1(10000, 2);
auto r2 = [=](double money, int year)->double {return money * rate * year; };
r2(10000, 2);
return 0;
}
調(diào)試反匯編進(jìn)行觀察:
當(dāng)創(chuàng)建Rate對象的時(shí)候是構(gòu)造函數(shù),使用Rate對象的時(shí)候就是會(huì)調(diào)用 Rate 類的 operator() 運(yùn)算符重載函數(shù);
而Lambda 表達(dá)式實(shí)際也是這樣子的:會(huì)調(diào)用 <lambda_uuid> 類的構(gòu)造函數(shù),在使用Rate r2對象時(shí),會(huì)調(diào)用<lambda_uuid>類的 operator()運(yùn)算符重載函數(shù)。
lambda表達(dá)式和范圍for是類似的,它們在語法層面上看起來都很神奇,但實(shí)際范圍for底層就是通過迭代器實(shí)現(xiàn)的,lambda表達(dá)式底層的處理方式和函數(shù)對象是一樣的。我們定義一個(gè)lambda表達(dá)式后,編譯器會(huì)自動(dòng)生成一個(gè)類,在該類中對operator () 運(yùn)算符進(jìn)行重載.
注意:lambda表達(dá)式之間不能賦值,每個(gè)lambda表達(dá)式的類型都是不同的(在VS下,lambda表達(dá)式會(huì)被處理為函數(shù)對象,該函數(shù)對象對應(yīng)的類名叫做<lambda_uuid>
。),這也是lambda表達(dá)式之間不能相互賦值的原因文章來源:http://www.zghlxwxcb.cn/news/detail-797331.html
int main()
{
int x = 10, y = 20;
auto Swap1 = [&x,&y]()
{
int tmp = x;
x = y;
y = tmp;
};
auto Swap2 = [&x,&y]()
{
int tmp = x;
x = y;
y = tmp;
};
cout << typeid(Swap1).name() << endl;
cout << typeid(Swap2).name() << endl;
return 0;
}
文章來源地址http://www.zghlxwxcb.cn/news/detail-797331.html
到了這里,關(guān)于【C++】C++11——lambda表達(dá)式的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!