深入理解C++11:探索lambda函數(shù)的奧秘
一、C++98中的排序
在 C++98 中,如果要對(duì)一個(gè)數(shù)據(jù)集合中的元素進(jìn)行排序,可以使用 std::sort 方法,下面代碼是對(duì)一個(gè)整型集合進(jìn)行排序。
#include <algorithm> #include <functional> #include <iostream> using namespace std; int main() { int array[] = { 4,1,8,5,3,7,0,9,2,6 }; cout << "原始數(shù)組:"; for (auto e : array) { cout << e << ' '; } cout << endl << endl << "排升序:"; // 默認(rèn)按照小于比較,排出來結(jié)果是升序 std::sort(array, array + sizeof(array) / sizeof(array[0])); for (auto e : array) { cout << e << ' '; } cout << endl << endl << "排降序:"; // 如果需要降序,需要改變?cè)氐谋容^規(guī)則 std::sort(array, array + sizeof(array) / sizeof(array[0]), greater<int>()); for (auto e : array) { cout << e << ' '; } return 0; }
小Tips:上面的 greater
是一個(gè)仿函數(shù),這里傳遞仿函數(shù)是用來控制大小比較的。關(guān)于仿函數(shù),在前面的文章中已經(jīng)多次使用,在【C++雜貨鋪】?jī)?yōu)先級(jí)隊(duì)列的使用指南與模擬實(shí)現(xiàn)一文中,我們使用仿函數(shù)來進(jìn)行大小比較;在【C++雜貨鋪】一文帶你走進(jìn)哈希:哈希沖突 | 哈希函數(shù) | 閉散列 | 開散列一文中,我們使用仿函數(shù)來獲取 pair<K, V> 模型中的 key??傊潞瘮?shù)有著十分強(qiáng)大的功能。
如果待排序的元素為自定義類型,由于自定義類型中可能有多重不同的屬性,因此需要用戶自己來定義排序時(shí)的比較規(guī)則:
struct Goods { string _name; // 名字 double _price; // 價(jià)格 int _evaluate; // 評(píng)價(jià) Goods(const char* str, double price, int evaluate) :_name(str) , _price(price) , _evaluate(evaluate) {} }; ostream& operator<<(ostream& out, Goods& goods) { out << goods._name << '-' << goods._price << '-' << goods._evaluate; return out; } struct ComparePriceLess { bool operator()(const Goods& gl, const Goods& gr) { return gl._price < gr._price; } }; struct ComparePriceGreater { bool operator()(const Goods& gl, const Goods& gr) { return gl._price > gr._price; } }; struct CompareevaluateLess { bool operator()(const Goods& gl, const Goods& gr) { return gl._evaluate < gr._evaluate; } }; struct CompareevaluateGreater { bool operator()(const Goods& gl, const Goods& gr) { return gl._evaluate > gr._evaluate; } }; int main() { vector<Goods> v = { { "蘋果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2, 3 }, { "菠蘿", 1.5, 4 } }; cout << "排序前:"; for (auto e : v) { cout << e << ' '; } cout << endl << endl << "按照價(jià)格排升序:"; sort(v.begin(), v.end(), ComparePriceLess()); for (auto e : v) { cout << e << ' '; } cout << endl << endl << "按照價(jià)格排降序:"; sort(v.begin(), v.end(), ComparePriceGreater()); for (auto e : v) { cout << e << ' '; } cout << endl << endl << "按照評(píng)價(jià)排升序:"; sort(v.begin(), v.end(), CompareevaluateLess()); for (auto e : v) { cout << e << ' '; } cout << endl << endl << "按照評(píng)價(jià)排降序:"; sort(v.begin(), v.end(), CompareevaluateGreater()); for (auto e : v) { cout << e << ' '; } cout << endl; }
小Tips:上面代碼中如果要使用庫(kù)里面的仿函數(shù) less
或 greater
,需要對(duì) >
、<
進(jìn)行運(yùn)算符重載,實(shí)現(xiàn) Goods
類的大小比較。但是上面的代碼中并沒有采取這種做法,而是直接自己寫了兩個(gè)仿函數(shù)進(jìn)行大小關(guān)系的比較。前面那種提供運(yùn)算符重載的方法比較局限,因?yàn)闊o論是 <
還是 >
都只能重載一份,即 operator<
和 operator>
各自只能在代碼中出現(xiàn)一份,且它們的內(nèi)部只能根據(jù)一種屬性進(jìn)行大小比較,在同一段代碼中不能實(shí)現(xiàn)根據(jù)不同屬性去排序。而自己寫仿函數(shù)就不會(huì)出現(xiàn)這種情況,我們可以在同一段代碼中根據(jù)不同的屬性去寫不同的仿函數(shù)(只要類名不同即可)。
隨著 C++ 語法的發(fā)展,人們開始覺得上面的寫法太復(fù)雜了,每次為了實(shí)現(xiàn)一個(gè)排序算法,都要重新去寫一個(gè)類(仿函數(shù),實(shí)現(xiàn)大小比較),如果每次比較的邏輯不一樣,還要去實(shí)現(xiàn)多個(gè)類,特別是相同類的命名,這些都給編程者帶來了極大的不便。因此,在 C++11 語法中出現(xiàn)了 Lambda 表達(dá)式。
二、先來看看 lambda 表達(dá)式長(zhǎng)什么樣
struct Goods { string _name; // 名字 double _price; // 價(jià)格 int _evaluate; // 評(píng)價(jià) 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 } }; cout << "排序前:"; for (auto e : v) { cout << e << ' '; } cout << endl << endl << "按照價(jià)格排升序:"; sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool {return g1._price < g2._price; }); for (auto e : v) { cout << e << ' '; } cout << endl << endl << "按照價(jià)格排降序:"; sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool {return g1._price > g2._price; }); for (auto e : v) { cout << e << ' '; } cout << endl << endl << "按照評(píng)價(jià)排升序:"; sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool {return g1._evaluate < g2._evaluate; }); for (auto e : v) { cout << e << ' '; } cout << endl << endl << "按照評(píng)價(jià)排降序:"; sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool {return g1._evaluate > g2._evaluate; }); for (auto e : v) { cout << e << ' '; } cout << endl; }
小Tips:上述代碼就是使用 C++11 中的 lambda 表達(dá)式來解決,可以看出 lambda 表達(dá)式實(shí)際是一個(gè)匿名函數(shù)對(duì)象。
三、lambda表達(dá)式語法
lambda 表達(dá)式書寫格式為:[capture-list](parameters) mutable-> return-type {statement}。
[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)行推斷。
{statement}:函數(shù)體。在該函數(shù)體內(nèi),除了可以使用其參數(shù)外,還可以使用所有捕捉到的變量。
小Tips:在 lambda 函數(shù)定義中,參數(shù)列表和返回值類型都是可選部分,而捕捉列表和函數(shù)體可以為空。因此 C++11 中最簡(jiǎn)單的 lambda 函數(shù)為:[]{};
。該 lambda 函數(shù)不能做任何事情。
實(shí)例:
int AddFunc(int x, int y) { return x + y; } int num1 = 10, num2 = 20; int main() { // 實(shí)現(xiàn)兩個(gè)數(shù)相加的 lambda 函數(shù) int a = 1, b = 10; auto add = [](int x, int y)->int {return x + y; }; cout << add(a, b) << endl; // 實(shí)現(xiàn)兩個(gè)函數(shù)交換的 lambda 函數(shù) auto swap = [add, a, b](int& x, int& y) { int tmp = x; x = y; y = tmp; // cout << add(a, b) << endl; // 在 lambda 函數(shù)的函數(shù)體中無法直接使用局部的 lambda 函數(shù)或者變量(對(duì)象). //cout << AddFunc(a, b) << endl; // 在 lambda 函數(shù)的函數(shù)體中可以直接使用全局的函數(shù)或者變量(對(duì)象). cout << AddFunc(num1, num2) << endl; // 在 lambda 函數(shù)的函數(shù)體中可以直接使用全局的函數(shù)或者變量(對(duì)象). }; swap(a, b); return 0; }
小Tips:在 lambda 函數(shù)的函數(shù)體中可以調(diào)用全局的函數(shù),使用全局的變量。但是要想在 lambda 的函數(shù)體中使用局部的變量或?qū)ο?,則需要通過捕捉列表或者參數(shù)列表。
3.1 捕捉列表的使用細(xì)節(jié)
捕捉列表描述了上下文中哪些數(shù)據(jù)可以被 lambda 使用,以及使用的方式是傳值還是傳引用。
[var]
:表示值傳遞方式捕捉變量 var。
[=]
:表示值傳遞方式捕捉所有父作用域中的變量(包括this)。
[&var]
:表示引用傳遞捕捉變量 var。
[&]
:表示引用方式傳遞捕捉所有父作用域中的變量(包括this)。
[this]
:表示值傳遞方式捕捉當(dāng)前的 this 指針。
小Tips:
父作用域指包含 lambda 函數(shù)語句塊的作用域。
值傳遞方式捕捉到的變量本質(zhì)上是父作用域中變量的一份拷貝,在默認(rèn)情況下會(huì)對(duì)捕捉到的變量加上 const
屬性,即不可以在 lambda 函數(shù)體中對(duì)捕捉到的變量進(jìn)行修改。如果想修改可以加上 mutable
取消其常量性。即 [x, y]
捕捉列表中的 x
和 y
是父作用域中 x
和 y
的一份拷貝,并且默認(rèn)給捕捉列表中的 x
和 y
加上了 const
屬性。
引用傳遞方式既可以捕捉普通的變量也可以捕捉 const
變量。
語法上捕捉列表可以由多個(gè)捕捉項(xiàng)組成,并以逗號(hào)隔開。例如:[=, &a, &b]
:以引用傳遞的方式捕捉變量 a
和 b
,以值傳遞方式捕捉其他所有變量;[&, a, this]
:以值傳遞方式捕捉變量 a
和 this
,以引用方式捕捉其他變量。如果由多個(gè)捕捉項(xiàng)組成,=
和 &
只能出現(xiàn)在捕捉列表的開頭,即 [&a, &b, = ]
這樣寫是錯(cuò)誤的。
捕捉列表不允許變量重復(fù)傳遞,否則就會(huì)導(dǎo)致編譯出錯(cuò)。例如:[=, a]
:= 已經(jīng)以值傳遞的方式捕捉了所有變量,在去捕捉 a
就會(huì)導(dǎo)致重復(fù)捕捉。
在塊作用域以外的 lambda 函數(shù)捕捉列表必須為空。
在塊作用域中的 lambda 函數(shù)僅能捕捉父作用域中的局部變量,捕捉任何非此作用域或者 非局部變量都會(huì)導(dǎo)致編譯報(bào)錯(cuò)。
lambda 表達(dá)式之間不能相互賦值,即使看起來類型相同。
四、lambda 的底層原理
lambda 看起來很厲害,但它本質(zhì)上就是仿函數(shù)。
int main() { auto func1 = [](int x, int y) {return x + y; }; auto func2 = [](int x, int y) {return x + y; }; cout << typeid(func1).name() << endl; cout << typeid(func2).name() << endl; func1(1, 2); return 0; }
如上面代碼所示,兩個(gè)仿函數(shù)對(duì)象 func1
和 func2
它們看起來是一模一樣的,但是通過打印它們各自的類型可以看出,它們的類型有所不同,因此 func1
和 func2
本質(zhì)上就是兩個(gè)不同的類對(duì)象,所以 lambda 表達(dá)式之間不能相互賦值,即使看起來類型相同。func1(1, 2)
本質(zhì)上就是 func1
這個(gè)仿函數(shù)的對(duì)象在調(diào)用 operator()
。
到此這篇關(guān)于深入理解C++11:探索lambda函數(shù)的奧秘的文章就介紹到這了,更多相關(guān)C++11 lambda函數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C++11中l(wèi)ambda、std::function和std:bind詳解
- C++11/14 線程中使用Lambda函數(shù)的方法
- 淺談C++11新引入的lambda表達(dá)式
- 結(jié)合C++11新特性來學(xué)習(xí)C++中l(wèi)ambda表達(dá)式的用法
- 淺析C++11新特性的Lambda表達(dá)式
- 一文讀懂c++11 Lambda表達(dá)式
- C++11 lambda表達(dá)式在回調(diào)函數(shù)中的使用方式
- C++11?lambda(匿名函數(shù))表達(dá)式詳細(xì)介紹
- C++11中的可變參數(shù)模板/lambda表達(dá)式
- 深入解析C++11?lambda表達(dá)式/包裝器/線程庫(kù)
相關(guān)文章
Qt實(shí)現(xiàn)小功能之復(fù)雜抽屜效果詳解
在Qt自帶的控件中,也存在抽屜控件:QToolBar。但是,該控件有個(gè)缺點(diǎn):一次只能展開一個(gè)抽屜信息,無法實(shí)現(xiàn)多個(gè)展開。所以本文將自定義實(shí)現(xiàn)復(fù)雜抽屜效果,需要的可以參考一下2022-10-10C++面試八股文之std::string實(shí)現(xiàn)方法
這篇文章主要介紹了C++面試八股文:std::string是如何實(shí)現(xiàn)的,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-06-06讓應(yīng)用程序只運(yùn)行一個(gè)實(shí)例的實(shí)現(xiàn)方法
我們?cè)谑褂谩?60軟件管家》時(shí)發(fā)現(xiàn),在《360軟件管家》已經(jīng)運(yùn)行了的情況下,再次點(diǎn)擊《360軟件管家》的圖標(biāo),那么它不會(huì)再運(yùn)行另外一個(gè)《360軟件管家》,而是將已有的《360軟件管家》給激活,始終只能運(yùn)行一個(gè)《360軟件管家》的實(shí)例2013-05-05C++?STL實(shí)現(xiàn)非變易查找算法的示例代碼
C++?STL?中的非變易算法(Non-modifying?Algorithms)是指那些不會(huì)修改容器內(nèi)容的算法,是C++提供的一組模板函數(shù),下面我們就來看看這一算法的應(yīng)用吧2023-08-08C語言:利用指針編寫程序,用梯形法計(jì)算給定的定積分實(shí)例
今天小編就為大家分享一篇C語言:利用指針編寫程序,用梯形法計(jì)算給定的定積分實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-12-12