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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++11 lambda表达式、function类模板、bind函数适配器

發布時間:2024/4/11 c/c++ 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++11 lambda表达式、function类模板、bind函数适配器 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • lambda表達式
    • lambda表達式的語法
    • lambda表達式的原理
  • function模板
    • function與重載函數
  • bind函數適配器


lambda表達式

當我們在寫代碼的時候如果經常需要用到一個公用的功能,為了方便維護,通常就會通過重載類的operator ()來將其寫成仿函數(具有函數特性的類)來使用。

從使用的角度來講,仿函數雖然好用,但是如果需要根據多種不同的情況設計多個函數的時候,這時候使用仿函數無疑是一種折磨,不僅僅代碼量增加,而且還有大量的代碼冗余。

例如下面這種情況
在我們使用購物網站的時候,通常都可以選擇以不同的屬性例如名字、價格、好評度、銷量等來為商品進行排序

struct Goods {string _name; // 名字double _price; // 價格int _num; // 數量// ... };/* 如果我們要使用sort來對貨物進行排序,就必須要提供給他一個比較的規則, 也就是用仿函數來實現一個大小判斷,但是我們并不知道他要按照哪種方式進行比較, 也不知道他是按照>、<、>=、<=等方式進行排序,在屬性很多的情況下,代碼就會出現下面這樣的大量冗余 */ struct ComparePriceGreater {bool operator()(const Goods& gl, const Goods& gr){return gl._price > gr._price;} };struct CompareNumGreater {bool operator()(const Goods& gl, const Goods& gr){return gl._num > gr._num;} };struct CompareNameGreater {bool operator()(const Goods& gl, const Goods& gr){return gl._name > gr._name;} };//..................等等等等

這樣的代碼不僅有上面的缺點,他的可讀性也非常的差,簡單的邏輯還好,倘若復雜的話接手代碼的人一定心態爆炸

所以C++11引入了lambda表達式來解決這個問題

同樣的代碼,在引入了lambda表達式后代碼的可讀性就大大的提升了。

sort(gds, gds + sizeof(gds) / sizeof(gds[0]), [](const Goods& g1, const Goods& g2)->bool{return g1._price > g2._price; });sort(gds, gds + sizeof(gds) / sizeof(gds[0]), [](const Goods& g1, const Goods& g2)->bool{return g1._price < g2._price; });sort(gds, gds + sizeof(gds) / sizeof(gds[0]), [](const Goods& g1, const Goods& g2)->bool{return g1._num > g2._num; });sort(gds, gds + sizeof(gds) / sizeof(gds[0]), [](const Goods& g1, const Goods& g2)->bool{return g1._num < g2._num; });

lambda表達式的語法

lambda表達式書寫格式:

[capture-list] (parameters) mutable -> returntype { statement }

lambda表達式各部分說明
[capture-list] : 捕捉列表,該列表總是出現在lambda函數的開始位置,編譯器根據[]來判斷接下來
的代碼是否為lambda函數,捕捉列表能夠捕捉上下文中的變量供lambda函數使用,捕捉列表描述了上下文中那些數據可以被lambda使用,以及使用的方式傳值還是傳引用。

捕獲列表:

  • [var]:表示值傳遞方式捕捉變量var
  • [=]:表示值傳遞方式捕獲所有父作用域中的變量(包括this)
  • [&var]:表示引用傳遞捕捉變量var
  • [&]:表示引用傳遞捕捉所有父作用域中的變量(包括this)
  • [this]:表示值傳遞方式捕捉當前的this指針

注意事項:

  • a. 父作用域指包含lambda函數的語句塊
  • b. 語法上捕捉列表可由多個捕捉項組成,并以逗號分割。 用方式捕捉其他變量 c. 捕捉列表不允許變量重復傳遞,否則就會導致編 譯錯誤。 比如:[=, a]:=已經以值傳遞方式捕捉了所有變量,捕捉a重復
  • d. 在塊作用域以外的lambda函數捕捉列表必須為空。
  • e. 在塊作用域中的lambda函數僅能捕捉父作用域中局部變量,捕捉任何非此作用域或者非局部變量都會導致編譯報錯。
  • f. lambda表達式之間不能相互賦值,即使看起來類型相同

(parameters):參數列表。與普通函數的參數列表一致,如果不需要參數傳遞,則可以連同()一起
省略
mutable:默認情況下,lambda函數總是一個const函數,mutable可以取消其常量性。使用該修
飾符時,參數列表不可省略(即使參數為空)。
->returntype:返回值類型。用追蹤返回類型形式聲明函數的返回值類型,沒有返回值時此部分
可省略。返回值類型明確情況下,也可省略,由編譯器對返回類型進行推導。
{statement}:函數體。在該函數體內,除了可以使用其參數外,還可以使用所有捕獲到的變量。

在lambda函數定義中,參數列表和返回值類型都是可選部分,而捕捉列表和函數體可以為空。
因此C++11中最簡單的lambda函數為:[]{}; 該lambda函數不能做任何事情。

例如使用lambda表達式來實現一個reverse

//因為lambda表達式本質是一個匿名函數,是他自己唯一的未命名類型,所以需要使用auto來進行類型推導 auto reverse = [](string& str){int begin = 0, end = str.size() - 1;while (begin < end){std::swap(str[begin++], str[end--]);} };int main() {//使用變量名調用即可string str = "HelloWorld";reverse(str);return 0; }

lambda表達式的原理

下面來看看lambda表達式在匯編下的調用過程

轉入反匯編進行查看,當我們調用lambda表達式時,可以看到編譯器在底層調用了lamber_uuid類(uuid為中間的編號)的operator(),這個做法是不是有點眼熟,這就是仿函數的做法。
下面與仿函數進行一個對比

struct Reverse {void operator()(string& str){int begin = 0, end = str.size() - 1;while (begin < end){std::swap(str[begin++], str[end--]);}} }; int main() {string str = "HelloWorld";Reverse()(str);return 0; }


無論是從使用上看,還是從底層的匯編代碼來看,lambda表達式都與仿函數幾乎一模一樣,所以由此可以推斷出lambda表達式的原理,當定義了lambda表達式之后,編譯器按照仿函數的形式自動生成一個lamber_uuid類,并重載其中的operator(),當用戶進行調用的時候會自動通過該類的匿名對象來調用,使得其看起來和普通的函數一樣。


function模板

C++中可調用對象(如函數指針,仿函數,lambda表達式等)的雖然都有一個比較統一的操作形式,但是定義方法五花八門,這樣就導致使用統一的方式保存可調用對象或者傳遞可調用對象時,會十分繁瑣。C++11中提供了std::function和std::bind統一了可調用對象的各種操作。

例如

// 普通函數 int add(int a, int b){return a+b;} // lambda表達式 auto mod = [](int a, int b){ return a % b;}// 仿函數 struct divide{int operator()(int denominator, int divisor){return denominator/divisor;} };

上面的幾種不同的可調用對象雖然類型不同,但是根據參數和返回值可以共享同一種調用形式int(int ,int)
通過function就可以統一其調用形式

std::function<int(int ,int)> a = add; std::function<int(int ,int)> b = mod ; std::function<int(int ,int)> c = divide();

定義格式:std::function<返回值(參數列表)> 名字

  • std::function 是一個可調用對象包裝器,是一個類模板,可以容納除了類成員函數指針之外的所有可調用對象,它可以用統一的方式處理函數、函數對象、函數指針,并允許保存和延遲它們的執行。
  • std::function可以取代函數指針的作用,因為它可以延遲函數的執行,特別適合作為回調函數使用。它比普通函數指針更加的靈活和便利。

function與重載函數

在使用function的時候還有一個需要注意的點,就是我們不能將重載過的函數直接放入function對象中,因為會有二義性的問題
例如

int add(int x, int y); double add(double x, double y);map<string, function<int(int, int)>> map; map.insert({"+", add});//此時無法判斷是哪個add//所以此時就不能直接使用函數名進行插入,可以通過使用函數指針來指向對應函數,再通過函數指針插入來消除二義性 int (*func)(int, int) = add; map.insert({"+", func}); 在這里插入代碼片

bind函數適配器

對于那些只在少量地方使用的簡單操作,使用lambda表達式時非常好的,但是如果使用的地方多了,就應該定義一個函數,而不是多次編寫相同的lambda表達式。而庫函數提供的模板參數又大多是以一個可調用的對象來進行接收,而如果需要將函數寫出仿函數的形式,又增添了不少麻煩,所以C++11提供了一個通用的函數適配器bind,它接受一個可調用對象,生成一個新的可調用對象來“適應”原對象的參數列表。(其實就是創建一個對象,將提供的函數作為其operator()的重載)

std::bind將可調用對象與其參數一起進行綁定,綁定后的結果可以使用std::function保存。std::bind主要有以下兩個作用:

  • 將可調用對象和其參數綁定成一個仿函數;
  • 只綁定部分參數,減少可調用對象傳入的參數。

bind的語法如下

bind (callable, arg_list); /*返回值為綁定完成的可調用對象參數callable為需要綁定的函數arg_list為參數列表 */

下面以reverse舉個例子

void reverse(string& str) {int begin = 0, end = str.size() - 1;while (begin < end){std::swap(str[begin++], str[end--]);} }int main() {string str = "HelloWorld";auto func = bind(reverse,std::placeholders::_1);func(str);cout << str << endl;return 0; }

在arg_list中通常還會包含形如_n的名字,這是位于中,std::placeholders::_n占位符,表示它們占據了傳遞給callable的第n個參數,_1為第一個,_2為第二個,以此類推。

例如我們可以利用占位符來固定住第2個參數,只讓用戶傳第一個參數

int add(int x, int y) {return x + y; }int main() {auto func = bind(add, std::placeholders::_1, 5);cout << func(4) << endl;//9return 0; }

如果需要綁定成員函數的時候,因為成員函數不會被隱式轉換為函數指針,還需要取地址,還需要其指明其來源的對象。
例如

struct Reverse {void reverse(string& str){int begin = 0, end = str.size() - 1;while (begin < end){std::swap(str[begin++], str[end--]);}} };int main() {Reverse r;auto func = bind(&Reverse::reverse, &r, std::placeholders::_1);string str = "helloworld";func(str);cout << str << endl;return 0; }

當需要傳遞的參數為引用時

bind的那些不是占位符的參數會被拷貝到bind返回的可調用對象中。但是,與lambda類似,有時對有些綁定的參數希望以引用的方式傳遞,或是要綁定參數的類型無法拷貝。這時候就需要借助到庫函數中的ref函數

ostream & print(ostream &os, const string& s, char c) {os << s << c;return os; }int main() {ostringstream os1;//ostringstream沒有拷貝構造,所以不能傳拷貝,只能傳引用bind(print, ref(os1), _1, c) }

總結

以上是生活随笔為你收集整理的C++11 lambda表达式、function类模板、bind函数适配器的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。