C++11中的lambda表達(dá)式與包裝器
lambda語法
lambda 表達(dá)式本質(zhì)是?個(gè)匿名函數(shù)對(duì)象,跟普通函數(shù)不同的是他可以定義在函數(shù)內(nèi)部。
lambda 表達(dá)式語法使?層??沒有類型,所以我們?般是?auto或者模板參數(shù)定義的對(duì)象去接收 lambda 對(duì)象
lambda表達(dá)式的格式: [capture-list] (parameters)-> return type {function boby }
- [capture-list] : 捕捉列表,該列表總是出現(xiàn)在 lambda 函數(shù)的開始位置,編譯器根據(jù)[]來判斷接下來的代碼是否為 lambda 函數(shù),捕捉列表能夠捕捉上?中的變量供 lambda 函數(shù)使用,捕捉列表可以傳值和傳引?捕捉。捕捉列表為空也不能省略。(捕捉列表只能是已經(jīng)定義的)
- (parameters) :參數(shù)列表,與普通函數(shù)的參數(shù)列表功能類似,如果不需要參數(shù)傳遞,則可以連同()?起省略
- ->return type :返回值類型,?追蹤返回類型形式聲明函數(shù)的返回值類型,沒有返回值時(shí)此部分可省略。?般返回值類型明確情況下,也可省略,由編譯器對(duì)返回類型進(jìn)?推導(dǎo)。
- {function boby} :函數(shù)體,函數(shù)體內(nèi)的實(shí)現(xiàn)跟普通函數(shù)完全類似,在該函數(shù)體內(nèi),除了可以使?其參數(shù)外,還可以使?所有捕獲到的變量,函數(shù)體為空也不能省略。
auto add1 = [](int x, int y)->int {return x + y; }; cout << add1(1, 2) << endl; // 1、捕捉為空也不能省略 // 2、參數(shù)為空可以省略 // 3、返回值可以省略,可以通過返回對(duì)象?動(dòng)推導(dǎo) // 4、函數(shù)題不能省略 auto func1 = [] { cout << "hello bit" << endl; return 0; };
捕捉列表
- lambda 表達(dá)式中默認(rèn)只能? lambda 函數(shù)體和參數(shù)中的變量,如果想用外層作?域中的變量就需要進(jìn)?捕捉
- 第?種捕捉?式是在捕捉列表中顯?的傳值捕捉和傳引?捕捉,捕捉的多個(gè)變量?逗號(hào)分割。[x,y, &z] 表?x和y值捕捉,z引?捕捉。
- 第?種捕捉?式是在捕捉列表中隱式捕捉,我們?cè)诓蹲搅斜韺?個(gè)=表?隱式值捕捉,在捕捉列表寫?個(gè)&表?隱式引?捕捉,這樣我們 lambda 表達(dá)式中?了那些變量,編譯器就會(huì)?動(dòng)捕捉那些變量
- 第三種捕捉?式是在捕捉列表中混合使?隱式捕捉和顯?捕捉。[=, &x]表?其他變量隱式值捕捉,x引?捕捉;[&, x, y]表?其他變量引?捕捉,x和y值捕捉。當(dāng)使用混合捕捉時(shí),第?個(gè)元素必須是&或=,并且&混合捕捉時(shí),后?的捕捉變量必須是值捕捉,同理=混合捕捉時(shí),后面的捕捉變量必須是引用捕捉。
- lambda 表達(dá)式如果在函數(shù)局部域中,他可以捕捉 lambda 位置之前定義的變量,不能捕捉靜態(tài)局部變量和全局變量,靜態(tài)局部變量和全局變量也不需要捕捉, lambda 表達(dá)式中可以直接使?。這也意味著 lambda 表達(dá)式如果定義在全局位置,捕捉列表必須為空。(但是一般都是將lambda定義在局部區(qū)域)
- 默認(rèn)情況下, lambda 捕捉列表是被const修飾的,也就是說傳值捕捉的過來的對(duì)象不能修改,mutable加在參數(shù)列表的后?可以取消其常量性,也就說使?該修飾符后,傳值捕捉的對(duì)象就可以修改了,但是修改還是形參對(duì)象,不會(huì)影響實(shí)參。使?該修飾符后,參數(shù)列表不可省略(即使參數(shù)為空)。
auto func1 = [a, &b] { // 值捕捉的變量不能修改,引?捕捉的變量可以修改 //a++; b++; int ret = a + b; return ret; }; auto func2 = [=] { int ret = a + b + c; return ret; }; auto func6 = [] { int ret = x + m; return ret; }; auto func4 = [&, a, b] { //a++; //b++; c++; d++; return a + b + c + d; }; auto func5 = [=, &a, &b] { a++; b++; /*c++; d++;*/ return a + b + c + d; }; auto func7 = [=]()mutable { a++; b++; c++; };
但如果lambda是寫在類里面的,那么lambda是不能夠捕獲私有成員變量的,也是不能直接使用的,如果想使用需要捕獲this指針
class A { public: void f() { auto add1 = [this](int a,int b){return a + b + _a1 + _a2}; } private: int _a1 = 1; int _a2 = 2; };
lambda的應(yīng)用
在學(xué)習(xí) lambda 表達(dá)式之前,可調(diào)用對(duì)象只有函數(shù)指針和仿函數(shù)對(duì)象,函數(shù)指針的類型定義起來比較麻煩,仿函數(shù)要定義?個(gè)類,相對(duì)會(huì)比較麻煩。使用 lambda 去定義可調(diào)用對(duì)象,既簡單又方便。
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) { return g1._price < g2._price; });
因?yàn)?code>sort會(huì)將迭代器區(qū)間傳給后面的對(duì)象,所以需要兩個(gè)參數(shù)。
lambda的原理
- lambda 的原理和范圍for很像,編譯后從匯編指令層的角度看,壓根就沒有l(wèi)ambda 和范圍for這樣的東西。范圍for底層是迭代器,?lambda底層是仿函數(shù)對(duì)象,也就說我們寫了?個(gè)lambda 以后,編譯器會(huì)?成?個(gè)對(duì)應(yīng)的仿函數(shù)的類。
- 仿函數(shù)的類名是編譯按?定規(guī)則?成的,保證不同的 lambda ?成的類名不同,lambda參數(shù)/返回類型/函數(shù)體就是仿函數(shù)operator()的參數(shù)/返回類型/函數(shù)體, lambda 的捕捉列表本質(zhì)是?成的仿函數(shù)類的成員變量,也就是說捕捉列表的變量都是 lambda 類構(gòu)造函數(shù)的實(shí)參,當(dāng)然隱式捕捉,編譯器要看使?哪些就傳那些對(duì)象。
包裝器
function
std::function
是?個(gè)類模板,也是?個(gè)包裝器。 std::function
的實(shí)例對(duì)象可以包裝存儲(chǔ)其他的可以調(diào)?對(duì)象,包括函數(shù)指針、仿函數(shù)、 lambda 、 bind 表達(dá)式等,存儲(chǔ)的可調(diào)用對(duì)象被稱為 std::function
的?標(biāo)。若 std::function
不含?標(biāo),則稱它為空。調(diào)?空std::function
的?標(biāo)導(dǎo)致拋出 std::bad_function_call
異常
int f(int a, int b) { return a + b; } struct Fun { public: int operator()(int a,int b) { return (a + b) * 10; } }; int main() { auto lf = [](int a, int b) {return (a + b) * 30; }; function<int(int, int)>f1(f); //function<int(int, int)>f2(Fun());編譯器會(huì)將其解釋為函數(shù)聲明而不是變量定義,這被稱為"最令人煩惱的解析" function<int(int, int)>f2 = Fun(); function<int(int, int)>f3(lf); vector<function<int(int, int)>>Vf = { f,Fun(),lf }; cout << f1(2, 3) << endl; cout << f2(1, 2) << endl; cout << lf(4, 5) << endl; for (auto p : Vf) { cout << p(1, 2) << endl; } return 0; }
函數(shù)指針、仿函數(shù)、 lambda 等可調(diào)?對(duì)象的類型各不相同, std::function
的優(yōu)勢就是統(tǒng)?類型,對(duì)他們都可以進(jìn)?包裝,這樣在很多地?就?便聲明可調(diào)?對(duì)象的類型。
可以把function當(dāng)做把可調(diào)用對(duì)象進(jìn)行統(tǒng)一為一個(gè)類型,這樣就可以將其儲(chǔ)存到容器中。
如果包的是成員函數(shù)的話需要注意,由于類的成員函數(shù)的第一個(gè)參數(shù)是默認(rèn)為this指針,所以function實(shí)例化的類型需要有類的對(duì)象的地址或者是對(duì)象,即使傳的是對(duì)象,對(duì)象會(huì)使用.*
操作符對(duì)this指針進(jìn)行調(diào)用。
class Plus { public: Plus(int n = 10) :_n(n) {} static int plusi(int a, int b) { return a + b; } double plusd(double a, double b) { return (a + b) * _n; } private: int _n; }; int main() { // 包裝靜態(tài)成員函數(shù) // 成員函數(shù)要指定類域并且前?加&才能獲取地址 //靜態(tài)成員函數(shù)是可以不加& function<int(int, int)> f4 = &Plus::plusi; cout << f4(1, 1) << endl; // 包裝普通成員函數(shù) // 普通成員函數(shù)還有?個(gè)隱含的this指針參數(shù),所以綁定時(shí)傳對(duì)象或者對(duì)象的指針過去都可以 function<double(Plus*, double, double)> f5 = &Plus::plusd; Plus pd; cout << f5(&pd, 1.1, 1.1) << endl; function<double(Plus, double, double)> f6 = &Plus::plusd; cout << f6(pd, 1.1, 1.1) << endl; cout << f6(pd, 1.1, 1.1) << endl; function<double(Plus&&, double, double)> f7 = &Plus::plusd; cout << f7(move(pd), 1.1, 1.1) << endl; cout << f7(Plus(), 1.1, 1.1) << endl; }
bind
- bind 是?個(gè)函數(shù)模板,它也是?個(gè)可調(diào)?對(duì)象的包裝器,可以把他看做?個(gè)函數(shù)適配器,對(duì)接收的fn可調(diào)?對(duì)象進(jìn)?處理后返回?個(gè)可調(diào)?對(duì)象。 bind 可以?來調(diào)整參數(shù)個(gè)數(shù)和參數(shù)順序。bind 也在
<functional>
這個(gè)頭?件中。 - 調(diào)?bind的?般形式:
auto newCallable = bind(callable,arg_list)
; 其中newCallable本?是?個(gè)可調(diào)?對(duì)象,arg_list是?個(gè)逗號(hào)分隔的參數(shù)列表,對(duì)應(yīng)給定的callable的參數(shù)。當(dāng)我們調(diào)?newCallable時(shí),newCallable會(huì)調(diào)用callable,并傳給它arg_list中的參數(shù)。 - arg_list中的參數(shù)可能包含形如_n的名字,其中n是?個(gè)整數(shù),這些參數(shù)是占位符,表?newCallable的參數(shù),它們占據(jù)了傳遞給newCallable的參數(shù)的位置。數(shù)值n表??成的可調(diào)?對(duì)象中參數(shù)的位置:_1為newCallable的第?個(gè)參數(shù),_2為第?個(gè)參數(shù),以此類推。_1/_2/_3…這些占位符放到
placeholders
的?個(gè)命名空間中。
using placeholders::_1; using placeholders::_2; using placeholders::_3; int Sub(int a, int b) { return (a - b) * 10; } int main() { auto sub1 = bind(Sub, _1, _2); cout << sub1(10, 5) << endl; // bind 本質(zhì)返回的?個(gè)仿函數(shù)對(duì)象 // 調(diào)整參數(shù)順序(不常?) // _1代表第?個(gè)實(shí)參 // _2代表第?個(gè)實(shí)參 // ... auto sub2 = bind(Sub, _2, _1); cout << sub2(10, 5) << endl; // 調(diào)整參數(shù)個(gè)數(shù) (常?) auto sub3 = bind(Sub, 100, _1); cout << sub3(5) << endl; auto sub4 = bind(Sub, _1, 100); cout << sub4(5) << endl; // 分別綁死第123個(gè)參數(shù) auto sub5 = bind(SubX, 100, _1, _2); cout << sub5(5, 1) << endl; cout << bind(SubX, 100, _1, _2)(5, 1) << endl; auto sub6 = bind(SubX, _1, 100, _2); cout << sub6(5, 1) << endl; auto sub7 = bind(SubX, _1, _2, 100); cout << sub7(5, 1) << endl; return 0; }
bind對(duì)于我們之前用function來包裝成員函數(shù)時(shí)第一個(gè)參數(shù)必須傳對(duì)象的情況做出了改變,可以用bind來綁定第一個(gè)參數(shù),這樣在后續(xù)調(diào)用該可調(diào)用對(duì)象的時(shí)候就可以不用傳對(duì)象了
// 成員函數(shù)對(duì)象進(jìn)?綁死,就不需要每次都傳遞了 function<double(Plus&&, double, double)> f6 = &Plus::plusd; Plus pd; cout << pd.plusd(1.1, 1.1) << endl; cout << f6(move(pd), 1.1, 1.1) << endl; cout << f6(Plus(), 1.1, 1.1) << endl; auto f = bind(&Plus::plusd, &pd, _1, _2); function<double(double, double)> f2 = bind(&Plus::plusd, &pd, _1, _2); cout << f(1.1,1.1) << endl; cout << f2(1.1, 1.1) << endl;
到此這篇關(guān)于C++11之lambda表達(dá)式與包裝器的文章就介紹到這了,更多相關(guān)C++ lambda表達(dá)式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用C語言實(shí)現(xiàn)內(nèi)存池的示例代碼
所謂內(nèi)存池,顧名思義和線程池的設(shè)計(jì)原理是一樣的,為了減少頻繁申請(qǐng)釋放內(nèi)存而帶來的資源消耗,減少釋放內(nèi)存后產(chǎn)生的內(nèi)存碎片,下面我們就來看看如何使用C語言實(shí)現(xiàn)內(nèi)存池吧2024-02-02如何寫好C main函數(shù)的幾個(gè)注意事項(xiàng)
這篇文章主要介紹了如何寫好C main函數(shù)的幾個(gè)注意事項(xiàng),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06C++ OpenCV實(shí)戰(zhàn)之網(wǎng)孔檢測的實(shí)現(xiàn)
這篇文章主要介紹了如何利用C++和OpenCV實(shí)現(xiàn)網(wǎng)孔檢測,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)OpenCV有一定幫助,感興趣的小伙伴可以了解一下2022-05-05C語言中的數(shù)組指針數(shù)組與函數(shù)指針數(shù)組
數(shù)組指針數(shù)組和函數(shù)指針數(shù)組是兩個(gè)強(qiáng)大但容易混淆的概念,下面就來介紹一下C語言中的數(shù)組指針數(shù)組與函數(shù)指針數(shù)組的區(qū)別,具有一定的參考價(jià)值,感興趣的可以了解一下2025-06-06