各位大佬好,我是落羽!一個堅持不斷學習進步的學生。
如果您覺得我的文章還不錯,歡迎多多互三分享交流,一起學習進步!
也歡迎關注我的blog主頁: 落羽的落羽
文章目錄
- 一、 什么是lambda表達式
- 二、 表達式語法
- 三、lambda的應用
一、 什么是lambda表達式
C++中有“可調用對象”的概念,主要包括函數指針、仿函數、lambda表達式。lambda表達式本質是一個匿名函數對象,跟普通函數不同的是,他可以定義在函數內部。
lambda表達式在語法使用層面上“沒有類型”,簡單來說:lambda表達式的類型是編譯器即時生成的、唯一的、匿名的,程序員無法在代碼中直接寫出它的類型名稱。lambda表達式使用時本質是被轉換成一個仿函數再使用的,每個lambda表達式都會被編譯器轉換成一個獨一無二的、匿名的類(仿函數)。即使兩個lambda看起來完全一樣,編譯器也會為它們生成兩個不同的類。所以,我們一般都是用auto或者模板參數定義的對象去接收lambda對象。
二、 表達式語法
lambda表達式的語法格式是:[capture-list](parameters)->return-type {function body}
(parameters)
:參數列表。與普通的函數參數列表功能類似,如果不需要傳參,則可以連同()
一起省略。->return-type
:return type是lambda函數的返回值類型。如果沒有返回值,那么->return type
這部分就可以省略,或者返回值類型明確的情況下,能由編譯器自動推導返回類型出來,也能省略這部分。{function body}
:函數體。函數體的實現與普通函數完全一樣,函數體內除了能使用參數列表外,還可以使用捕捉列表內的變量。函數體為空也必須寫{}
。[capture-list]
:捕捉列表。該列表寫在lambda表達式的開始位置,編譯器根據[]
來判斷接下來的代碼是否為lambda函數。捕捉列表能夠捕捉上下文的變量,以供lambda函數內部使用,捕捉列表可以傳值或傳引用,捕捉列表就算為空也必須寫[]
。
舉幾個栗子:
auto add = [](int x, int y){ return x + y; };
auto func = []{cout << "hello world" << endl;};
auto swap = [](int& a, int& b){int tmp = a;a = b;b = tmp;};
非常好理解吧!
關于捕捉列表,還有一些使用的細節:
lambda表達式中默認只能使用參數列表中的變量或lambda內定義的,如果想使用外層作用域中的變量就需要進行捕捉。
- 第一種方式是顯式捕捉,在捕捉列表中可以顯式傳值捕捉和傳引用捕捉,捕捉的多個變量用逗號分隔,比如
[x, y, &z]
表示x和y進行傳值捕捉,lambda內部改變x和y不會改變原變量,而z是傳引用捕捉,對z的改變會改變原變量。 - 第二種捕捉方式是隱式捕捉,我們在[ ]中寫一個 = 表示隱式值捕捉,寫一個 & 表示隱式引用捕捉。這樣我們的lambda的表達式中使用了哪些外部變量,編譯器就會自動捕捉那些變量。
- 第三種捕捉列表是混合捕獲,可以組合使用隱式捕獲和顯式捕獲,實現更精細的控制。[=, &x] 表示x進行引用捕捉,其余變量都為值捕捉;[&, x, y] 表示x和y進行值捕捉,其余變量進行引用捕捉。使用混合捕捉時,第一個元素必須是=或&,第一個是=時,后面的捕捉變量必須是引用捕捉;第一個是&時,后面的捕捉變量必須是值捕捉。
lambda表達式中如果在局部域時,可以捕捉到它之前定義的變量,不能捕捉靜態局部變量和全局變量,因為這兩種變量本來就不需要捕捉也能在內部直接使用,lambda表達式內部可以直接使用。lambda表達式如果定義在全局位置,捕捉列表必須為空。
默認情況下,lambda捕捉列表是const屬性的,也就是說傳值捕捉來的對象不能被修改,在參數列表后加上修飾符mutable
就可以取消其常性,但是修改還是改變的形參對象,不會影響實參。使用該修飾符后參數列表不能省略。
int x = 0;
// 捕捉列表必須為空,因為全局變量不?捕捉就可以?,沒有可被捕捉的變量
auto func1 = [](){x++;};int main()
{// 只能?當前lambda局部域和捕捉的對象和全局對象int a = 0, b = 1, c = 2, d = 3;auto func1 = [a, &b]{// 值捕捉的變量不能修改,引?捕捉的變量可以修改//a++;b++;int ret = a + b;return ret;};cout << func1() << endl;// 隱式值捕捉// ?了哪些變量就捕捉哪些變量auto func2 = [=]{int ret = a + b + c;return ret;};cout << func2() << endl;// 隱式引?捕捉// ?了哪些變量就捕捉哪些變量auto func3 = [&]{a++;c++;d++;};func3();cout << a << " " << b << " " << c << " " << d << endl;// 混合捕捉1auto func4 = [&, a, b]{//a++;//b++;c++;d++;return a + b + c + d;};func4();cout << a << " " << b << " " << c << " " << d << endl;// 混合捕捉1auto func5 = [=, &a, &b]{a++;b++;/*c++;d++;*/return a + b + c + d;};func5();cout << a << " " << b << " " << c << " " << d << endl;// 局部的靜態和全局變量不能捕捉,也不需要捕捉static int m = 0;auto func6 = []{int ret = x + m;return ret;};// 傳值捕捉本質是?種拷?,并且被const修飾了// mutable相當于去掉const屬性,可以修改了// 但是修改了不會影響外?被捕捉的值,因為是?種拷?auto func7 = [=]()mutable{a++;b++;c++;d++;return a + b + c + d;};cout << func7() << endl;cout << a << " " << b << " " << c << " " << d << endl;return 0;
}
三、lambda的應用
學習lambda表達式之前,我們使用的可調用對象只有函數指針和仿函數,它們的定義都相對麻煩,使用lambda表達式去定義可調用對象,就十分簡單方便了。
vector<pair<int, int>> v = { {1,2}, {3,4}, {8,3}, {5,1} };
//遇到這樣的場景,假如我們想要用多種排序規則進行排序,以前是需要寫不同的仿函數傳給sort/*struct Compare_first
{bool operator()(const pair<int, int>& p1, const pair<int, int>& p2){return p1.first < p2.first;}
};
struct Compare_second
{bool operator()(const pair<int, int>& p1, const pair<int, int>& p2){return p1.second < p2.second;}
};
sort(v.begin(), v.end(), Compare_first());
sort(v.begin(), v.end(), Compare_second());*///有了lambda表達式后,就方便許多了
sort(v.begin(), v.end(), [](const pair<int, int>& p1, const pair<int, int>& p2) {return p1.first < p2.first; });
for (auto pa : v)
{cout << pa.first << ":" << pa.second << " ";
}
cout << endl;sort(v.begin(), v.end(), [](const pair<int, int>& p1, const pair<int, int>& p2) {return p1.second < p2.second; });
for (auto pa : v)
{cout << pa.first << ":" << pa.second << " ";
}
cout << endl;
本篇完,感謝閱讀。