日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Lambda表达式用法超详细整理!!!

發(fā)布時(shí)間:2024/3/12 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Lambda表达式用法超详细整理!!! 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章較長,堅(jiān)持看完,相信對你一定會有所收獲!

Lambda我們可以將其理解為一個(gè)未命名的內(nèi)聯(lián)函數(shù)。
與任何函數(shù)類似,一個(gè)lambda具有一個(gè)返回類型,一個(gè)參數(shù)列表和一個(gè)函數(shù)體。
但與函數(shù)不同,lambda可能定義在函數(shù)內(nèi)部。
一個(gè)lambda表達(dá)式具有如下形式:
[capture list] (parameter list) ->return type {function body}
capture list: 捕獲列表,是一個(gè)lambda所在函數(shù)中定義的局部變量列表(通常為空)
parameter list:參數(shù)列表
return type:返回類型
function body:函數(shù)體
但是與普通函數(shù)不同,lambda必須使用尾置返回來指定返回類型
我們可以忽略參數(shù)列表和返回類型,但必須永遠(yuǎn)包含捕獲列表和函數(shù)體

auto f=[]{return 42;};//分號不能丟

此例中,我們定義了一個(gè)可調(diào)用對象f,它不接受參數(shù),返回42.
lambda的調(diào)用方式與普通函數(shù)調(diào)用方式相同,都是使用調(diào)用運(yùn)算符:

cout<<f()<<endl;//打印42

在lambda中忽略括號和參數(shù)列表等價(jià)于指定一個(gè)空參數(shù)列表。在此例中,當(dāng)調(diào)用f時(shí),參數(shù)列表是空的。如果忽略返回類型,lambda根據(jù)函數(shù)體中的代碼推斷出返回類型。
如果函數(shù)體只是一個(gè)return語句,則返回類型從返回的表達(dá)式的類型推斷而來,否則,返會類型為void.
如果lambda的函數(shù)體包含任何一個(gè)單一的return語句之外的內(nèi)容,且未指定返回類型,則返回void

向lambda傳遞參數(shù)

與一個(gè)普通函數(shù)調(diào)用類似,調(diào)用一個(gè)lambda時(shí)給定的實(shí)參被用來初始化lambda的形參。通常,實(shí)參和形參的類型必須匹配。但與普通函數(shù)不同,lambda不能有默認(rèn)參數(shù)。
因此,一個(gè)lambda調(diào)用的實(shí)參數(shù)目永遠(yuǎn)與形參數(shù)目相等。
下面舉一個(gè)帶參數(shù)的lambda的例子:

[]const string &a,const string& b) {return a.size()<b.size();};//這里分號不能丟

空捕獲列表表面此lambda不使用它所在函數(shù)中的任何局部變量。

使用捕獲列表

雖然一個(gè)lambda可以出現(xiàn)在一個(gè)函數(shù)中,使用其局部變量,但它只能使用那些明確指明的變量。一個(gè)lambda通過將局部變量包含在其捕獲列表中來指明將會使用這些變量。捕獲列表指引lambda在其內(nèi)部包含訪問局部變量所需的信息。
下面舉一個(gè)例子:

#include<iostream> using namespace std; void test() {string sz("abc");string words("abds");auto ret=[sz](const string& a){return a.size() >= sz.size();};cout << ret("efgdasd") << endl; } int main() {test();system("pause");return 0; }


lambda以一對[]開始,我們可以在其中提供一個(gè)以逗號分隔的名字列表(當(dāng)前函數(shù)中定義的局部變量),這些名字都是它所在函數(shù)中定義的。
上面例子中由于lambda只捕獲了test函數(shù)中局部變量sz,因此可以在lambda的函數(shù)體中使用sz.lambda不捕獲words,因此不能在lambda的函數(shù)體中訪問此變量。如果我們給lambda提供一個(gè)空捕獲列表,則代碼會編譯出錯(cuò):

一個(gè)lambda只有在其捕獲列表中捕獲了一個(gè)它所在函數(shù)中的局部變量,才能在函數(shù)體中使用該變量

lambda表達(dá)式在泛型算法的應(yīng)用

與find_if結(jié)合使用
舉例:調(diào)用find_if算法在字符串s中查找第一個(gè)長度大于等于字符串sz的元素

#include <iostream> #include <vector> #include <algorithm> using namespace std; //用lambda作為參數(shù) void bigger(vector<string>& words,vector<string>::size_type sz) {vector<string>::iterator pos = find_if(words.begin(), words.end(), [sz](const string& a) {return a.size() > sz; });cout << *pos << endl; } int main() { vector<string> svec{ "the", "quick", "red", "fox", "jumps","over", "the", "slow", "red", "turtle" };bigger(svec, 4); }


這里對find_if的調(diào)用返回一個(gè)迭代器,指向第一個(gè)長度不小于sz的元素。如果這樣的元素不存在,則返回words.end()的一個(gè)拷貝
我們可以使用find_if返回的迭代器來計(jì)算從它開始到words的末尾一共有多少個(gè)元素。

//用lambda作為參數(shù) void bigger(vector<string>& words,vector<string>::size_type sz) {vector<string>::iterator pos = find_if(words.begin(), words.end(), [sz](const string& a) {return a.size() > sz; });//這里兩個(gè)迭代器做減法,類似指針做減法,得到兩個(gè)迭代器之間的距離//與指針不同,我們無法直接打印迭代器,例如cout<<pos<<endl;auto count = words.end() - pos;cout << count<< endl; }



與for_each結(jié)合使用
舉例:打印words中長度大于等于sz的元素。

//用lambda作為參數(shù) void bigger(vector<string>& words,vector<string>::size_type sz) {vector<string>::iterator pos = find_if(words.begin(), words.end(), [sz](const string& a) {return a.size() > sz; });for_each(pos, words.end(), [](const string& a) {cout << a << " "; }); } int main() { vector<string> svec{ "the", "quick", "red", "fox", "jumps","over", "the", "slow", "red", "turtle" };bigger(svec, 4); }


此lambda中的捕獲列表為空,但其函數(shù)體中還是使用了兩個(gè)名字:s和cout,前者是它自己的參數(shù)。
捕獲列表為空,是因?yàn)槲覀冎粚ambda所在的函數(shù)中定義的(非static)變量使用了捕獲列表。一個(gè)lambda可以直接使用定義在當(dāng)前函數(shù)之外的名字。在本例中,cout不是定義在bigger中的局部名字,而是定義在頭文件iostream中。因此,只要在bigger出現(xiàn)的作用域中包含頭文件iostream,我們的lambda就可以使用cout.
注意:捕獲列表只用于局部非static變量,lambda可以直接使用局部static變量和它所在函數(shù)之外聲明的名字
完整的biggerd代碼:

#include <iostream> #include <vector> #include <algorithm> using namespace std; //刪除重復(fù)元素 void elimDups(vector<string>& words) {//再刪除重復(fù)元素之前,需要先進(jìn)行排序sort(words.begin(), words.end());//把重復(fù)元素移到尾部auto new_end = unique(words.begin(), words.end());//將尾部重復(fù)元素刪除words.erase(new_end, words.end()); } //用lambda作為參數(shù) void bigger(vector<string>& words,vector<string>::size_type sz) {//將words按字典序重排,并消除重復(fù)單詞elimDups(words);//按長度重新排序,長度相同的單詞維持字典序stable_sort(words.begin(), words.end(), [](const string& a1, const string& a2) {return a1.size() > a2.size(); });//獲取一個(gè)迭代器,指向第一個(gè)滿足size>=sz的元素vector<string>::iterator pos = find_if(words.begin(), words.end(), [sz](const string& a) {return a.size() > sz; });//計(jì)算滿足size>=sz的元素的數(shù)目auto count = count_if(words.begin(), words.end(), [sz](const string& a) {return a.size()>sz; });cout << count << endl;//打印長度大于等于給定值的單詞,每個(gè)單詞后面接一個(gè)空格for_each(pos, words.end(), [](const string& a) {cout << a << " "; }); } int main() { vector<string> svec{ "the", "quick", "red", "fox", "jumps","over", "the", "slow", "red", "turtle" };bigger(svec, 4); }


類似參數(shù)傳遞,變量的捕獲方式也可以是值或引用。

值捕獲

與傳值參數(shù)類似,采用值捕獲的前提是變量可以拷貝。
與參數(shù)不同,被捕獲的變量的值實(shí)在lambda創(chuàng)建時(shí)拷貝,而不是調(diào)用時(shí)拷貝
舉例:

#include <iostream> using namespace std; void test() {size_t v1 = 42;//局部變量//將v1拷貝到名為f的可調(diào)用對象auto f = [v1] {return v1; };v1 = 0;auto j = f();cout << j << endl;//j為42,f保存了我們創(chuàng)建它時(shí)的拷貝 } int main() { test();return 0; }


由于被捕獲的變量的值是在lambda創(chuàng)建時(shí)拷貝,因此隨后對其修改不會影響到lambda內(nèi)對應(yīng)的值

引用捕獲

舉例:

#include <iostream> using namespace std; void test() {size_t v1 = 42;//局部變量//f對象包含v1的引用auto f = [&v1] {return v1; };v1 = 0;auto j = f();cout << j << endl;//j為0,f保存了v1的引用,而非拷貝 } int main() { test();return 0; }


當(dāng)我們在lambda函數(shù)體內(nèi)使用此變量時(shí),實(shí)際上使用的時(shí)引用所綁定的對象。
引用捕獲和返回引用的注意事項(xiàng):
如果我們采用引用的方式捕獲了一個(gè)變量,就必須確保被引用的對象在lambda執(zhí)行的時(shí)候是存在的。
lambda捕獲的都是局部變量,這些變量在函數(shù)結(jié)束后就不復(fù)存在了。
如果lambda可能在函數(shù)結(jié)束后執(zhí)行,捕獲的引用執(zhí)行的局部變量已經(jīng)消失。
引用捕獲有時(shí)候是必要的,例如:

#include <iostream> #include <vector> #include <algorithm> using namespace std; void bigger(vector<string>& words ,ostream &os=cout,char ch=' ') {for_each(words.begin(), words.end(), [&os, ch](const string& s) {os << s << ch; }); } int main() { vector<string> svec{ "the", "quick", "red", "fox", "jumps","over", "the", "slow", "red", "turtle" };bigger(svec); }


我們不能拷貝ostream對象,因此捕獲os的唯一方法就是捕獲其引用(或指向os的指針)。
注意:當(dāng)以引用方式捕獲一個(gè)變量的時(shí)候,必須保證lambda指向時(shí)變量是存在的

對lambda變量捕獲部分重點(diǎn)總結(jié):

捕獲一個(gè)普通變量,如int,string或其他非指針類型,通常采用簡單的值捕獲方式。
如果我們捕獲一個(gè)指針或迭代器,或采用引用捕獲方式,就必須保證對象具有預(yù)期的值。
在lambda從創(chuàng)建到它執(zhí)行這段時(shí)間內(nèi),可能有代碼改變綁定對象的值。
也就是說,在該指針(或引用)被捕獲的時(shí)刻,綁定的對象的值是我們所期望的,但在lambda執(zhí)行時(shí),該對象的值已經(jīng)完全不同了。
一般來說,我們應(yīng)該盡量減少捕獲的數(shù)據(jù)量,來避免潛在的捕獲導(dǎo)致的問題。而且,如果有可能的話,應(yīng)該避免捕獲指針或引用。

隱式捕獲

通過在捕獲列表中寫一個(gè)&或=,指示編譯器推斷捕獲列表。
&告訴編譯器采用引用方式,=則表示采用值捕獲方式
例如:

#include <iostream> #include <vector> #include <algorithm> using namespace std; void bigger(vector<string>& words,vector<string>::size_type sz) {//sz為隱式捕獲,值捕獲方式auto wc = find_if(words.begin(), words.end(), [=](const string& s) {return s.size() >= sz; });cout << *wc << endl; } int main() { vector<string> svec{ "the", "quick", "red", "fox", "jumps","over", "the", "slow", "red", "turtle" };bigger(svec, 4); }


如果我們希望對一部分變量采用值捕獲,對其他變量采用引用捕獲,可以混合使用隱式捕獲和顯示捕獲:

#include <iostream> #include <vector> #include <algorithm> using namespace std; void bigger(vector<string>& words,vector<string>::size_type sz,ostream& os=cout,char c=' ') {//os為隱式捕獲,引用捕獲方式//c顯示捕獲,值捕獲方式for_each(words.begin(), words.end(), [&, c](const string& s) {os << s << c; });cout << endl;//os顯示捕獲,引用捕獲方式//c隱式捕獲,值捕獲方式for_each(words.begin(), words.end(), [=,&os](const string& s) {os << s << c; }); } int main() { vector<string> svec{ "the", "quick", "red", "fox", "jumps","over", "the", "slow", "red", "turtle" };bigger(svec, 4); }


當(dāng)我們混合使用隱式捕獲和顯示捕獲時(shí),捕獲列表中的第一個(gè)元素必須是一個(gè)&或=,此符號指定了默認(rèn)捕獲方式為引用或值
當(dāng)混合使用隱式捕獲和顯示捕獲時(shí),顯示捕獲的變量必須使用與隱式捕獲不同的方式。
即如果隱式不會是引用方式,則顯示捕獲命名變量必須采用值方式,因此不能在其名字前使用&.
類似的,如果隱式捕獲采用的是值方式,則顯示捕獲命名的變量必須采用引用方式,即在名字前使用&。

總結(jié)lambda捕獲列表:

1、空。沒有使用任何函數(shù)對象參數(shù)。
2、=。函數(shù)體內(nèi)可以使用Lambda所在作用范圍內(nèi)所有可見的局部變量(包括Lambda所在類的this),并且是值傳遞方式(相當(dāng)于編譯器自動(dòng)為我們按值傳遞了所有局部變量)。
3、&。函數(shù)體內(nèi)可以使用Lambda所在作用范圍內(nèi)所有可見的局部變量(包括Lambda所在類的this),并且是引用傳遞方式(相當(dāng)于編譯器自動(dòng)為我們按引用傳遞了所有局部變量)。
4、this。函數(shù)體內(nèi)可以使用Lambda所在類中的成員變量。
5、a。將a按值進(jìn)行傳遞。按值進(jìn)行傳遞時(shí),函數(shù)體內(nèi)不能修改傳遞進(jìn)來的a的拷貝,因?yàn)槟J(rèn)情況下函數(shù)是const的。要修改傳遞進(jìn)來的a的拷貝,可以添加mutable修飾符。
6、&a。將a按引用進(jìn)行傳遞。
7、a, &b。將a按值進(jìn)行傳遞,b按引用進(jìn)行傳遞。
8、=,&a, &b。除a和b按引用進(jìn)行傳遞外,其他參數(shù)都按值進(jìn)行傳遞。
9、&, a, b。除a和b按值進(jìn)行傳遞外,其他參數(shù)都按引用進(jìn)行傳遞。

可變lambda

默認(rèn)情況下,對于一個(gè)值被拷貝的變量,lambda不會改變其值,如果我們希望能改變一個(gè)被捕獲的變量的值,就必須在參數(shù)列表首加上關(guān)鍵字mutable。因此,可變lambda能省略參數(shù)列表:

#include <iostream> using namespace std; void test() {size_t val = 42;//局部變量//f可以改變它所捕獲的變量的值auto f = [val]()mutable {return val++; };val = 0;cout << f() << endl;cout << f() << endl;cout << f() << endl;cout << val << endl; } int main() {test();return 0; }


多次調(diào)用函數(shù)f,就可以多次對f保存的val進(jìn)行累加改變。
但val本身值不變
一個(gè)引用捕獲的變量是否可以修改依賴與此引用指向的是一個(gè)const類型還是一個(gè)非const類型:

#include <iostream> using namespace std; void test() {size_t val = 42;//局部變量//val是一個(gè)非const變量的引用//可以通過f中的引用來改變它auto f = [&val]() {return val++; };val = 0;cout << f() << endl;cout << f() << endl;cout << f() << endl;cout << val << endl; } int main() {test();return 0; }


指定lambda的返回類型

默認(rèn)情況下,如果一個(gè)lambda體包含return之外的任何語句,則編譯器假定此lambda返回void.
與其他返回void的函數(shù)類似,被推斷為返回void的lambda不能有返回值。
舉例:

#include <iostream> #include<algorithm> using namespace std; void test() {int arr[5] = { -1,-2,-3,-4,-5 };transform(arr, arr + 5,arr,[](int i) {return i < 0 ? -i : i; });for (int i = 0; i < 5; i++){cout << arr[i];}cout << endl; } int main() {test();return 0; }


本例中l(wèi)ambda體中只有單一的return語句。我們無需指定返回類型,因?yàn)榭梢愿鶕?jù)條件運(yùn)算符的類型推斷出來。
但是如果我們將程序改寫成看起來是等價(jià)的if語句,就會產(chǎn)生編譯錯(cuò)誤:

雖然這里沒有發(fā)生錯(cuò)誤,是因?yàn)榘姹締栴},有些低版本編譯器會出現(xiàn)問題,原因在于:
編譯器推斷這個(gè)版本的lambda返回類型為void,但它返回一個(gè)int值。
當(dāng)我們需要為一個(gè)lambda定義一個(gè)返回類型時(shí),必須使用尾置返回類型:

#include <iostream> #include<algorithm> using namespace std; void test() {int arr[5] = { -1,-2,-3,-4,-5 };transform(arr, arr + 5, arr, [](int i)->int{if (i < 0) return -i;else return i;});for (int i = 0; i < 5; i++){cout << arr[i];}cout << endl; } int main() {test();return 0; }

lambda是函數(shù)對象----->函數(shù)對象—>重載()運(yùn)算符—>operator()

當(dāng)我們編寫了一個(gè)lambda后,編譯器將該表達(dá)式翻譯成一個(gè)未命名類的未命名對象。
在lambda表達(dá)式產(chǎn)生的類中含有一個(gè)重載的函數(shù)調(diào)用運(yùn)算符。
舉例:

#include <iostream> #include <vector> #include <algorithm> using namespace std; void bigger(vector<string>& words,vector<string>::size_type sz) {//根據(jù)單詞的長度對其進(jìn)行排序stable_sort(words.begin(), words.end(), [](const string& a1, const string& a2) {return a1.size()<a2.size(); });for_each(words.begin(), words.end(), [](const string& a) {cout << a << " "; }); } int main() { vector<string> svec{ "the", "quick", "red", "fox", "jumps","over", "the", "slow", "red", "turtle" };bigger(svec, 4); }


該lambda的行為類似于下面這個(gè)類的一個(gè)未命名對象
函數(shù)對象的概念
仿函數(shù)做泛型算法的參數(shù)又細(xì)分為一元謂詞和二元謂詞,不了解的建議去看看:
謂詞的概念

#include <iostream> #include <vector> #include <algorithm> using namespace std; class ShortString { public:bool operator()(const string& a1, const string& a2){return a1.size() < a2.size();} }; void bigger(vector<string>& words,vector<string>::size_type sz) {//根據(jù)單詞的長度對其進(jìn)行排序stable_sort(words.begin(), words.end(), ShortString());for_each(words.begin(), words.end(), [](const string& a) {cout << a << " "; }); } int main() { vector<string> svec{ "the", "quick", "red", "fox", "jumps","over", "the", "slow", "red", "turtle" };bigger(svec, 4); }


默認(rèn)情況下lambda不能改變他捕獲的變量。是因?yàn)橛蒷ambda產(chǎn)生的類中的函數(shù)調(diào)用的運(yùn)算符是一個(gè)const成員函數(shù)。如果lambda被聲明為可變的,則調(diào)用運(yùn)算符就不是const的了。

表示lambda及相應(yīng)捕獲行為的類

當(dāng)一個(gè)lambda表達(dá)式通過引用捕獲變量時(shí),將由程序負(fù)責(zé)確保lambda執(zhí)行時(shí)引用所引的對象確實(shí)存在。因此,編譯器可以直接使用該引用而無需在lambda產(chǎn)生的類中將其存儲為數(shù)據(jù)成員。
相反,通過值捕獲的變量被拷貝到lambda中。因此,這種lambda產(chǎn)生的類必須為每個(gè)值捕獲的變量建立對應(yīng)的數(shù)據(jù)成員,同時(shí)創(chuàng)建構(gòu)造函數(shù),令其使用捕獲捕獲的變量的值來初始化數(shù)據(jù)成員。
舉個(gè)例子:

void bigger(vector<string>& words,vector<string>::size_type sz) {auto wc = find_if(words.begin(), words.end(), [sz](const string& a) {return a.size() >= sz; }); }

該lambda表達(dá)式的產(chǎn)生的類的將形如:

class SizeComp { private:size_t sz;//該數(shù)據(jù)成員對應(yīng)通過值捕獲的變量 public:SizeComp(size_t n):sz(n){}//該形參對應(yīng)的捕獲變量//該調(diào)用運(yùn)算符的返回類型,形參和函數(shù)體都與lambda一致bool operator()(const string& s)const{return s.size() >= sz;} };

上面這個(gè)類含有一個(gè)數(shù)據(jù)成員以及一個(gè)用于初始化該成員的構(gòu)造函數(shù)。
這個(gè)合成的類不含有默認(rèn)構(gòu)造函數(shù),因此想使用這個(gè)類必須提供一個(gè)實(shí)參:

void bigger(vector<string>& words,vector<string>::size_type sz) {auto wc = find_if(words.begin(), words.end(), SizeComp(sz)); }

lambda表達(dá)式產(chǎn)生的類不含默認(rèn)構(gòu)造函數(shù),賦值運(yùn)算符及默認(rèn)析構(gòu)函數(shù);
它是否含有默認(rèn)的拷貝/移動(dòng)構(gòu)造函數(shù)則通常要視捕獲的數(shù)據(jù)成員類型而定。

完結(jié)撒花!!!

總結(jié)

以上是生活随笔為你收集整理的Lambda表达式用法超详细整理!!!的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。