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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

快用一用 lambda 表达式吧,让你的代码更简洁、更漂亮!

發布時間:2024/2/28 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 快用一用 lambda 表达式吧,让你的代码更简洁、更漂亮! 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

lambda 表達式

定義 lambda 表達式

捕獲子句

按值捕獲

按引用捕獲

捕獲特定的變量

捕獲this指針

結合 lambda 使用 STL 算法


lambda 表達式

lambda 表達式提供了一種便捷、簡潔的語法來快速定義回調函數或函數對象。而且,不只語法簡潔,lambda 表達式還允許在使用回調的地方定義回調的邏輯。這通常比在某個類定義的函數調用運算符的某個地方定義這種邏輯要好得多。因此,使用 lambda 表達式一般能夠得到極具表達力且可讀性仍然很好的代碼??聪旅孢@個例子:

bool compare(int a,int b){return a>b; } 使用函數 sort(asd.begin(), asd.end(), compare);使用lambda表達式 sort(asd.begin(), asd.end(), [](int &a, int &b)->bool {return a > b;});

lambda 表達式與函數定義有許多相似的地方。最基本的 lambda 表達式提供了一種定義沒有名稱的函數(即匿名函數)的方法。在應用中會發現 lambda 表達式與普通函數不同,因為 lambda 表達式可以訪問自己定義的作用域內存儲的變量。lambda 表達式的計算結果是一個函數對象。該函數對象的正式名稱是 lambda 閉包,但是有很多人也稱之為 lambda 函數。(?閉包就是能夠讀取其他函數內部變量的函數 )

?

?

定義 lambda 表達式

一個基本的 lambda 表達式:

[] (int x, int y) { return x < y; }

可以看出,lambda 表達式的定義看上去很像函數的定義。主要區別在于,lambda 表達式不指定返回類型和函數名稱,并且始終以方括號開頭。表達式開始的?" [ ] " 稱為 lambda 引導,它們標記了 lambda 表達式的開頭。lambda 引導的內容并不總是為空的。lambda 引導后跟的 " ( ) " 是 lambda 參數列表。對于沒有參數的?lambda 表達式,可以省略空的參數列表 ()。即可以將形式為 [](){......},的 lambda 表達式進一步縮減為 []{......}。空的 lambda 引導不能省略,因為它標記 lambda 表達式的開始。

有的人可能會覺得 lambda 表達式沒有函數名稱還可以理解,但是沒有返回類型是不是就有點過分了(玩呢?)。lambda 表達式可以包含任意數量的語句,返回類型默認為返回值的類型。如果沒有返回值,返回類型為 void。不過返回值類型也是可以根據需求設定的,例如下面這種情況:

[](double x) -> int { int y = x; return x-y;}

?

?

捕獲子句

lambda 引導 [] 不一定是空的,它可以包含捕獲子句,以指定封閉作用域中的變量如何在 lambda 表達式中訪問。如果方括號為空,則 lambda 表達式體只能使用 lambda 表達式中局部定義的實參和變量。沒有捕獲子句的 lambda 表達式稱為無狀態的 lambda 表達式,因為它不能訪問其封閉作用域中的任何內容。默認捕獲子句有兩種:= 和 &。捕獲子句只能包含一種默認捕獲子句,不能同時包含兩者。

?

按值捕獲

如果在方括號中包含 =,lambda 表達式體就可以按值訪問封閉作用域中的所有自動變量,即這些變量的值可以在 lambda 表達式中使用,但不能修改存儲在原始變量中的值。當你按值捕獲變量時,又試圖改變它,編譯器就會報錯:

int a = 1,b = 2,c = 3; [ =,&c ](){ a++; b++; c++; cout << a << " " << b << " " << c <<endl; } (); cout << a << " " << b << " " << c <<endl;

= 捕獲子句允許在 lambda 表達式體中按值訪問 lambda 表達式定義所在作用域的所有變量。在上面的例子中,在原則上,lambda 表達式體能夠訪問 main() 的 3 個局部變量:a、b 和 c。按值捕獲局部變量的效果與按值傳遞實參大不相同。

不同點在于:對于 lambda 表達式體內用到的封閉作用域內的每個局部變量,閉包對象都有一個成員。稱為 lambda 捕獲了這些變量。至少在概念上,所生成的成員變量的名稱與捕獲到的變量的名稱相同。這樣一來,lambda 表達式體看起來訪問的是封閉作用域中的變量,但實際上訪問的是 lambda 閉包內存儲的對應的成員變量。

?

按引用捕獲

如果在方括號中放置 &,封閉作用域中的所有變量就可以按引用訪問,所以它們的值可以由 lambda 表達式體中的代碼修改。例如:

int a = 1,b = 2,c = 3; //按引用捕獲 [ & ](){ a++; b++; c++; } (); cout << a << " " << b << " " << c <<endl;

外層作用域內的所有變量都可按引用訪問,所以 lambda 可以使用和修改它們的值。雖然 & 捕獲子句是合法的,但是在外層作用域按引用捕獲所有變量并不是一種好方法,因為這將使得變量有可能被無意修改。類似地,使用 = 默認捕獲子句則可能引入高開銷的復制操作。因此,更安全的做法是顯式指定如何捕獲自己需要的每個變量。

?

捕獲特定的變量

通過在捕獲子句中逐個列舉,可以指定想要訪問的封閉作用域中的特定變量。對于每個變量,可以選擇按值還是按引用捕獲。在變量名前面加上 &,就可以按引用捕獲變量。例如:

auto counter { [ &count] (int x, int y) {++count; return x < y;} };

count 是封閉作用域中可以在 lambda 表達式體中訪問的唯一變量,&count 規范使之可以按引用訪問。沒有 &,外層作用域中的 count 變量就按值訪問。當你想要按值捕獲特定變量時,不能在變量名前面加 = 作為前綴。例如,捕獲子句 [ = numbers ] 是無效的,正確的語法是 [ numbers ]。

在捕獲子句中放置多個變量時,就用逗號分開它們。可以自由混合按值捕獲的變量和按引用捕獲的變量。還可以在捕獲子句中,同時包含默認捕獲子句和捕獲的特定變量名稱。例如:捕獲子句 [ =, &counter ] 允許按引用訪問 counter,按值訪問封閉作用域中的其他變量。類似的,捕獲子句 [ &, numbers ] 的意思是,按值捕獲 numbers,按引用捕獲其他變量。如果指定默認捕獲子句(?= 或 &),則它必須是捕獲列表中的第一項。

注意:如果使用 = 默認捕獲子句,則不能再按值捕獲任何特定變量;類似地,如果使用 &,則不能再按引用捕獲特定變量。例如:[ =, = a ] 或 [ &, & b ]。

?

捕獲this指針

當我們在類中的成員函數中使用 lambda 表達式時,這個情況和之前的不一樣,問題在于只有局部變量和函數實參才能按值或引用捕獲,而類的成員變量不能按值或按引用捕獲。以下是在類的成員函數中使用 lambda 的情況:

可以通過對捕獲子句添加關鍵字 this ,讓 lambda 表達式訪問類的成員。通過捕獲 this 指針,實際上就使得 lambda 表達式能夠訪問包含它的成員函數所能訪問的所有成員,也就是說,盡管 lambda 閉包不屬于類,但其函數調用運算符仍然能夠訪問類的 protected 和 private 數據成員。lambda 表達式還能夠訪問所有成員函數,無論它們被聲明為 public、protected 還是 private。

class Asd {private:int a, b;public:Asd(){a = 1;b = 2; }int geta(){return a;}int getb(){return b;}void print(){auto asd = [this](){ return getb() - geta();} ;cout << "getb() - geta():" << asd() <<endl;}void print1(){auto asd = [=](){ return b - a;} ;cout << "b-a:" << asd() <<endl;} };

注意:= 默認捕獲子句已經暗示著(按值)捕獲this指針。因此這條語句也是合法的:

auto asd = [=](){ return b - a;} ;

但不允許將默認捕獲子句 = 與 this 結合使用 (至少在 C++17 中不允許,C++20 中可能會有變化)。因此,編譯器將把 [=, this] 這種形式的捕獲子句標記為錯誤。不過,允許使用[&, this],因為 & 并不暗示著捕獲 this。

?

捕獲子句小總結:

[] 不捕獲任何變量 [=] 用值的方式捕獲所有變量 [&] 以引用方式捕獲所有變量 [asd] 以值方式捕獲asd; 不捕獲其它變量 [=,&asd] 以引用捕獲asd, 但其余變量都靠值捕獲 [&, asd] 以值捕獲asd, 但其余變量都靠引用捕獲 [this] 捕獲所在類的this指針

?

完整代碼:

#include <iostream> using namespace std;class Asd {private:int a, b;public:Asd(){a = 1;b = 2; }int geta(){return a;}int getb(){return b;}void print(){auto asd = [this](){ return getb() - geta();} ;cout << "getb() - geta():" << asd() <<endl;}void print1(){auto asd = [=](){ return b - a;} ;cout << "b-a:" << asd() <<endl;} };int main() {//不捕獲任何變量[] { cout << "Study lambda!" <<endl; } ();auto print = [] { cout << "Study lambda!" <<endl; }; print();//根據需求確定返回值auto asd = [](double x) -> int { int y = x; return x-y;};cout << asd(3.6) <<endl;int a = 1,b = 2,c = 3;//按值捕獲[ = ](){ cout << a << " " << b << " " << c <<endl; } ();//按引用捕獲[ & ](){ a++; b++; c++; } ();cout << a << " " << b << " " << c <<endl;//捕獲特定的變量[ =,&c ](){ c++; cout << a << " " << b << " " << c <<endl; } ();cout << a << " " << b << " " << c <<endl;[ &,a ](){ b++; c++; cout << a << " " << b << " " << c <<endl; } ();cout << a << " " << b << " " << c <<endl;//捕獲this指針Asd asd1, asd2;asd1.print();asd2.print1();return 0; }

?

?

結合 lambda 使用 STL 算法

接下來將從?距離?和?簡潔?兩個方面探討使用 lambda 的優勢。

很多人認為,讓定義位于使用的地方附近很有用。這樣,在閱讀源代碼時,就無需去找該函數功能的具體代碼。例如,調用 count_if() 的第三個參數時,不需要向前尋找具體實現。另外,如果需要修改代碼,涉及的內容都在附近。從這一角度出發,lambda 是理想的選擇,因為其定義和使用是在同一個地方進行的。而函數可能存在一種比較糟糕的情況,即其函數內部使用了其他函數,而這些函數可能在不同的地方,這在閱讀源碼這一點上是非常費時、費力的。

從簡潔的角度看,函數和 lambda 的簡潔程度相當,一個例外是,需要使用同一個 lambda 兩次:

count1 = count_if ( a.begin(), a.end(), [] (int x) { return x % 3 == 0; }); count2 = count_if ( b.begin(), b.end(), [] (int x) { return x % 3 == 0; });

但并不一定要編寫 lambda 兩次,而是給 lambda 指定一個名稱,并使用該名稱兩次:

auto mod3 = [] (int x) { return x % 3 == 0; } count1 = count_if ( a.begin(), a.end(), mod3 ); count2 = count_if ( b.begin(), b.end(), mod3 );

甚至可以像使用常規函數那樣使用有名稱的 lambda:

bool result = mod3(z);

然而,不同于常規函數,可在函數內部定義有名稱的 lambda 。mod3 的實際類型隨實現而異,它取決于編譯器使用什么類型來跟蹤 lambda 。

代碼:

#include <iostream> #include <vector> #include <algorithm> using namespace std;bool compare(int a, int b) {return a > b; }int main() { vector<int> asd;for (int i = 0; i < 10; ++i)asd.push_back(i);sort(asd.begin(), asd.end(), [](int &a, int &b)->bool {return a > b;});sort(asd.begin(), asd.end(), compare);for (int i = 0; i < asd.size(); ++i)cout << asd[i] << endl;return 0; }

?

代碼:

#include <iostream> #include <numeric> #include <vector>void print_container(const std::vector<char>& c) {for (auto x : c) {std::cout << x << ' ';}std::cout << '\n'; }int main() {std::vector<char> cnt(10);std::iota(cnt.begin(), cnt.end(), '0');std::cout << "Init:\n";print_container(cnt);std::erase(cnt, '3');std::cout << "Erase \'3\':\n";print_container(cnt);auto erased = std::erase_if(cnt, [](char x) { return (x - '0') % 2 == 0; });std::cout << "Erase all even numbers:\n";print_container(cnt);std::cout << "In all " << erased << " even numbers were erased.\n"; }

?

總結

以上是生活随笔為你收集整理的快用一用 lambda 表达式吧,让你的代码更简洁、更漂亮!的全部內容,希望文章能夠幫你解決所遇到的問題。

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