C++11中的可變參數(shù)模板/lambda表達(dá)式
1.可變參數(shù)模板
C++11的新特性可變參數(shù)模板能夠讓我們創(chuàng)建可以接受可變參數(shù)的函數(shù)模板和類模板,相比C++98和C++03,類模板和函數(shù)模板中只能含固定數(shù)量的模板參數(shù),可變參數(shù)模板無(wú)疑是一個(gè)巨大的改進(jìn)??墒强勺儏?shù)模板比較抽象,因此這里只會(huì)寫(xiě)出夠我們使用的部分。
下面是一個(gè)基本可變參數(shù)的函數(shù)模板
// Args是一個(gè)模板參數(shù)包,args是一個(gè)函數(shù)形參參數(shù)包 // 聲明一個(gè)參數(shù)包Args...args,這個(gè)參數(shù)包中可以包含0到任意個(gè)模板參數(shù)。 template <class ...Args> void ShowList(Args... args) {}
上面的參數(shù)args前面有省略號(hào),所以它就是一個(gè)可變模版參數(shù),我們把帶省略號(hào)的參數(shù)稱為“參數(shù)
包”,它里面包含了0到N(N>=0)個(gè)模版參數(shù)。我們無(wú)法直接獲取參數(shù)包args中的每個(gè)參數(shù)的,只能通過(guò)展開(kāi)參數(shù)包的方式來(lái)獲取參數(shù)包中的每個(gè)參數(shù),這是使用可變模版參數(shù)的一個(gè)主要特點(diǎn),也是最大的難點(diǎn),即如何展開(kāi)可變模版參數(shù)。
遞歸函數(shù)方式展開(kāi)參數(shù)包
//遞歸終止函數(shù) template <class T> void showList(const T& t) { cout << y << endl; } //展開(kāi)函數(shù) template<class T,class ...Args> void showList(T value, Args... args) { cout << value << " "; showList(args...); } int main() { showList(1); showList(1,'A'); showList(1,'A',std::string("sort")); return 0; }
代碼分析:
①showList(1): 如果只有一個(gè)參數(shù),那會(huì)直接調(diào)用第一個(gè)showList函數(shù)。
②showList(1,'A'): 匹配到第二個(gè)showList函數(shù)后,先將1打印出來(lái)。然后通過(guò)遞歸,看看args里面有多少個(gè)參數(shù),如果只有一個(gè),比如這里的'A',那么就會(huì)去調(diào)用第一個(gè)showList函數(shù)。
③showList(1,'A',"sort"): 匹配到第二個(gè)showList函數(shù)后,先將1打印出來(lái)。然后通過(guò)遞歸,看看args里面有多少個(gè)參數(shù),這里有兩個(gè),那么繼續(xù)調(diào)用第二個(gè)showList函數(shù),此時(shí)的value變成了'A',依次類推。
逗號(hào)表達(dá)式展開(kāi)參數(shù)包
這種展開(kāi)參數(shù)包的方式,不需要通過(guò)遞歸終止函數(shù),是直接在expand函數(shù)體中展開(kāi)的, printarg不是一個(gè)遞歸終止函數(shù),只是一個(gè)處理參數(shù)包中每一個(gè)參數(shù)的函數(shù)。這種就地展開(kāi)參數(shù)包的方式實(shí)現(xiàn)的關(guān)鍵是逗號(hào)表達(dá)式。我們知道逗號(hào)表達(dá)式會(huì)按順序執(zhí)行逗號(hào)前面的表達(dá)式。
expand函數(shù)中的逗號(hào)表達(dá)式:(printarg(args), 0),也是按照這個(gè)執(zhí)行順序,先執(zhí)行printarg(args),再得到逗號(hào)表達(dá)式的結(jié)果0。同時(shí)還用到了C++11的另外一個(gè)特性——初始化列表,通過(guò)初始化列表來(lái)初始化一個(gè)變長(zhǎng)數(shù)組, {(printarg(args), 0)...}將會(huì)展開(kāi)成((printarg(arg1),0),(printarg(arg2),0), (printarg(arg3),0), etc... ),最終會(huì)創(chuàng)建一個(gè)元素值都為0的數(shù)組int arr[sizeof...(Args)]。由于是逗號(hào)表達(dá)式,在創(chuàng)建數(shù)組的過(guò)程中會(huì)先執(zhí)行逗號(hào)表達(dá)式前面的部分printarg(args)打印出參數(shù),也就是說(shuō)在構(gòu)造int數(shù)組的過(guò)程中就將參數(shù)包展開(kāi)了,這個(gè)數(shù)組的目的純粹是為了在數(shù)組構(gòu)造的過(guò)程展開(kāi)參數(shù)包。
template <class T> void PrintArg(T t) { cout << t << " "; } //展開(kāi)函數(shù) template <class ...Args> void ShowList(Args... args) { int arr[] = { (PrintArg(args), 0)... }; cout << endl; } int main() { ShowList(1); ShowList(1, 'A'); ShowList(1, 'A', std::string("sort")); return 0; }
STL容器中的empalce相關(guān)接口函數(shù):
template <class... Args> void emplace_back (Args&&... args)
首先我們看到的emplace系列的接口,支持模板的可變參數(shù),并且萬(wàn)能引用。那么相對(duì)insert和
emplace系列接口的優(yōu)勢(shì)到底在哪里呢
int main() { std::list< std::pair<int, char> > mylist; // emplace_back支持可變參數(shù),拿到構(gòu)建pair對(duì)象的參數(shù)后自己去創(chuàng)建對(duì)象 // 那么在這里我們可以看到除了用法上,和push_back沒(méi)什么太大的區(qū)別 mylist.emplace_back(10, 'a'); mylist.emplace_back(20, 'b'); mylist.emplace_back(make_pair(30, 'c')); mylist.push_back(make_pair(40, 'd')); mylist.push_back({ 50, 'e' }); for (auto e : mylist) cout << e.first << ":" << e.second << endl; return 0; }
2.lambda表達(dá)式
在C++98中,如果想要對(duì)一個(gè)數(shù)據(jù)集合中的元素進(jìn)行排序,可以使用std::sort方法
int main() { int array[] = { 4,1,8,5,3,7,0,9,2,6 }; // 默認(rèn)按照小于比較,排出來(lái)結(jié)果是升序 std::sort(array, array + sizeof(array) / sizeof(array[0])); // 如果需要降序,需要改變?cè)氐谋容^規(guī)則 std::sort(array, array + sizeof(array) / sizeof(array[0]), greater<int>()); return 0; }
如果待排序元素為自定義類型,還需要我們定義排序時(shí)的比較規(guī)則。隨著C++語(yǔ)法的發(fā)展,人們開(kāi)始覺(jué)得這種寫(xiě)法太復(fù)雜了,每次為了實(shí)現(xiàn)一個(gè)algorithm算法,都要重新去寫(xiě)一個(gè)類,如果每次比較的邏輯不一樣,還要去實(shí)現(xiàn)多個(gè)類,特別是相同類的命名,這些都給編程者帶來(lái)了極大的不便。因此,在C++11語(yǔ)法中出現(xiàn)了Lambda表達(dá)式。
先來(lái)看看lambda表達(dá)式的例子:
//自定義類型 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 = { { "蘋(píng)果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2, 3 }, { "菠蘿", 1.5, 4 } }; //比較價(jià)格 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; }); //比較評(píng)價(jià) sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) { return g1._evaluate < g2._evaluate; }); sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) { return g1._evaluate > g2._evaluate; }); return 0; }
上述代碼就是使用C++11中的lambda表達(dá)式來(lái)解決,可以看出lambda表達(dá)式實(shí)際是一個(gè)匿名函數(shù)。
lambda表達(dá)式語(yǔ)法
ambda表達(dá)式書(shū)寫(xiě)格式:[capture-list] (parameters) mutable -> return-type { statement}
lambda表達(dá)式各部分說(shuō)明:
[capture-list] : 捕捉列表,該列表總是出現(xiàn)在lambda函數(shù)的開(kāi)始位置,編譯器根據(jù)[]來(lái)判斷接下來(lái)的代碼是否為lambda函數(shù),捕捉列表能夠捕捉上下文中的變量供lambda函數(shù)使用。
(parameters):參數(shù)列表。與普通函數(shù)的參數(shù)列表一致,如果不需要參數(shù)傳遞,則可以連同()一起省略
mutable:默認(rèn)情況下,lambda函數(shù)總是一個(gè)const函數(shù),mutable可以取消其常量性。使用該修飾符時(shí),參數(shù)列表不可省略(即使參數(shù)為空)。
->returntype:返回值類型。用追蹤返回類型形式聲明函數(shù)的返回值類型,沒(méi)有返回值時(shí)此部分可省略。返回值類型明確情況下,也可省略,由編譯器對(duì)返回類型進(jìn)行推導(dǎo)。
{statement}:函數(shù)體。在該函數(shù)體內(nèi),除了可以使用其參數(shù)外,還可以使用所有捕獲到的變量。
注意:
在lambda函數(shù)定義中,參數(shù)列表和返回值類型都是可選部分,而捕捉列表和函數(shù)體可以為空。因此C++11中最簡(jiǎn)單的lambda函數(shù)為:[]{}; 該lambda函數(shù)不能做任何事情。
int main() { // 最簡(jiǎn)單的lambda表達(dá)式, 該lambda表達(dá)式?jīng)]有任何意義 [] {}; // 省略參數(shù)列表和返回值類型,返回值類型由編譯器推導(dǎo)為int int a = 3, b = 4; [=] {return a + 3; }; // 省略了返回值類型,無(wú)返回值類型 auto fun1 = [&](int c) {b = a + c; }; fun1(10) cout << a << " " << b << endl; // 各部分都很完善的lambda函數(shù) auto fun2 = [=, &b](int c)->int {return b += a + c; }; cout << fun2(10) << endl; // 復(fù)制捕捉x int x = 10; auto add_x = [x](int a) mutable { x *= 2; return a + x; }; cout << add_x(10) << endl; return 0; }
通過(guò)上述例子可以看出,lambda表達(dá)式實(shí)際上可以理解為無(wú)名函數(shù),該函數(shù)無(wú)法直接調(diào)用,如果想要直接調(diào)用,可借助auto將其賦值給一個(gè)變量。
捕獲列表說(shuō)明:
捉列表描述了上下文中那些數(shù)據(jù)可以被lambda使用,以及使用的方式傳值還是傳引用。
[var]:表示值傳遞方式捕捉變量var
[=]:表示值傳遞方式捕獲所有父作用域中的變量(包括this)
[&var]:表示引用傳遞捕捉變量var
[&]:表示引用傳遞捕捉所有父作用域中的變量(包括this)
[this]:表示值傳遞方式捕捉當(dāng)前的this指針
注意:
a. 父作用域指包含lambda函數(shù)的語(yǔ)句塊
b. 語(yǔ)法上捕捉列表可由多個(gè)捕捉項(xiàng)組成,并以逗號(hào)分割。比如:[=, &a, &b]:以引用傳遞的方式捕捉變量a和b,值傳遞方式捕捉其他所有變量[&,a, this]:值傳遞方式捕捉變量a和this,引用方式捕捉其他變量
c. 捕捉列表不允許變量重復(fù)傳遞,否則就會(huì)導(dǎo)致編譯錯(cuò)誤。比如:[=, a]:=已經(jīng)以值傳遞方式捕捉了所有變量,捕捉a重復(fù)
d. 在塊作用域以外的lambda函數(shù)捕捉列表必須為空。
e. 在塊作用域中的lambda函數(shù)僅能捕捉父作用域中局部變量,捕捉任何非此作用域或者非局部變量都會(huì)導(dǎo)致編譯報(bào)錯(cuò)。
f. lambda表達(dá)式之間不能相互賦值,即使看起來(lái)類型相同
到此這篇關(guān)于C++11中的可變參數(shù)模板/lambda表達(dá)式的文章就介紹到這了,更多相關(guān)C++11 lambda表達(dá)式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++實(shí)現(xiàn)馬踏棋盤(pán)(騎士周游)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)馬踏棋盤(pán),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02C++ vector的介紹及常見(jiàn)功能實(shí)現(xiàn)
這篇文章主要介紹了C++ vector的介紹及模擬實(shí)現(xiàn),vector在實(shí)際中非常的重要,但在實(shí)際中我們只要熟悉常見(jiàn)的接口就可以了,最重要的是理解他的底層原理,要能夠自己模擬實(shí)現(xiàn)出一個(gè)簡(jiǎn)單的vector,本文結(jié)合示例代碼給大家詳細(xì)介紹,需要的朋友可以參考下2023-05-05C++基礎(chǔ)入門(mén)教程(六):為什么創(chuàng)建類的時(shí)候要用new?
這篇文章主要介紹了C++基礎(chǔ)入門(mén)教程(六):為什么創(chuàng)建類的時(shí)候要用new?本文講解了使用new創(chuàng)建動(dòng)態(tài)結(jié)構(gòu)體、為什么要有new、自動(dòng)存儲(chǔ)(自動(dòng)變量、局部變量)、動(dòng)態(tài)存儲(chǔ)、vector和array等內(nèi)容,需要的朋友可以參考下2014-11-11線段樹(shù)詳解以及C++實(shí)現(xiàn)代碼
線段樹(shù)在一些acm題目中經(jīng)常見(jiàn)到,這種數(shù)據(jù)結(jié)構(gòu)主要應(yīng)用在計(jì)算幾何和地理信息系統(tǒng)中,這篇文章主要給大家介紹了關(guān)于線段樹(shù)以及C++實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2021-07-07C++中范圍(Ranges)與視圖(Views)的常見(jiàn)問(wèn)題、易錯(cuò)點(diǎn)
ranges和views是C20引入的重要特性,它們讓代碼更加簡(jiǎn)潔、高效且富有表達(dá)力,通過(guò)理解其基本概念、注意常見(jiàn)的陷阱,并合理應(yīng)用高級(jí)技巧,開(kāi)發(fā)者可以充分利用這些新特性,提升軟件質(zhì)量和開(kāi)發(fā)效率,,本文將深入淺出地探討ranges與views的基礎(chǔ)概念、常見(jiàn)問(wèn)題、易錯(cuò)點(diǎn)及避免策略2024-06-06C++?std::copy與memcpy區(qū)別小結(jié)
本文主要介紹了C++?std::copy與memcpy區(qū)別小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-05-05C語(yǔ)言單鏈表實(shí)現(xiàn)學(xué)生管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言單鏈表實(shí)現(xiàn)學(xué)生管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-12-12Visual Studio Code (vscode) 配置C、C++環(huán)境/編寫(xiě)運(yùn)行C、C++的教程詳解(主要Windo
這篇文章主要介紹了Visual Studio Code (vscode) 配置C、C++環(huán)境/編寫(xiě)運(yùn)行C、C++(主要Windows、簡(jiǎn)要Linux),本文通過(guò)實(shí)例截圖給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03C/C++編譯報(bào)錯(cuò)printf was not declared in 
這篇文章主要介紹了C/C++編譯報(bào)錯(cuò)printf was not declared in this scope問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08C++中賦值運(yùn)算符與逗號(hào)運(yùn)算符的用法詳解
這篇文章主要介紹了C++中賦值運(yùn)算符與逗號(hào)運(yùn)算符的用法詳解,是C++入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-09-09