C++11?lambda(匿名函數(shù))表達式詳細介紹
前言
Lambda(匿名函數(shù))表達式是C++11最重要的特性之一,lambda來源于函數(shù)式編程的概念,也是現(xiàn)代編程語言的一個特點。
優(yōu)點如下:
- 聲明式編程風格:就地匿名定義目標函數(shù)或函數(shù)對象,有更好的可讀性和可維護性。
- 簡潔:不需要額外寫一個命名函數(shù)或函數(shù)對象,,避免了代碼膨脹和功能分散。
- 更加靈活:在需要的時間和地點實現(xiàn)功能閉包。
概念及基本用法
lambda表達式定義了一個匿名函數(shù),并且可以捕獲一定范圍內(nèi)的變量。語法形式如下:
[ capture ] ( params ) opt -> ret { body; };
- capture:捕獲列表
- params:參數(shù)列表
- opt:函數(shù)選項
- ret:返回值類型
- body:函數(shù)體
一個完整的lambda表達式是這樣:
auto f = [](int a) -> int {return a + 1;}; cout << f(3) << endl; //輸出4
以上定義了一個完整的lambda,但是在實際的使用中,可以省略其返回值的定義,編譯器會根據(jù)return語句進行自動推導返回值類型。
省略過后如下:
auto f = [](int a) {return a + 1;};
需要注意的是,初始化列表不能用于返回值的自動推導:
如:auto f = [](){return {1,2};}; //error:無法推導返回值類型
另外,如果表達式?jīng)]有參數(shù)列表時,也可以省略,如:
auto f = []{return 1;};
捕獲變量
lambda表達式可以通過捕獲列表捕獲一定范圍內(nèi)的變量,主要有以下幾種情況:
- [] 不捕獲任何變量
- [&]捕獲外部作用域中所有變量,并作為引用在函數(shù)體中使用(按引用捕獲)
- [=]捕獲外部作用域中所有變量,并作為副本在函數(shù)體重使用(按值捕獲)
- [=,&foo] 按值捕獲外部作用域中所有變量,并按引用捕獲foo變量
- [bar] 按值捕獲bar變量,同時不捕獲其他變量
- [this] 捕獲當前類中的this指針,讓表達式擁有和當前類成員函數(shù)同樣的訪問權限。如果已經(jīng)使用了&或者=,就默認添加此選項。捕獲this的目的是可以在lambda中使用當前類的成員變量和成員函數(shù)。
通過示例來看具體用法:
class A { public: int i_ = 0; void func(int x,int y) { auto x1 = []{return i_;}; // error,沒有捕獲外部變量 auto x2 = [=]{return i_ + x + y;}; //ok,按值捕獲所有外部變量 auto x3 = [&]{return i_ + x + y;}; //ok,按引用捕獲所有外部變量 auto x4 = [this]{return i_;}; //ok,捕獲this指針 auto x5 = [this]{return i_ + x + y;}; //error,沒有捕獲x和y變量 auto x6 = [this,x,y]{return i_ + x + y;}; //ok,捕獲了this指針和x、y變量 auto x7 = [this]{return i_++;}; //ok,捕獲了this指針,修改成員變量的值 } };
int a = 0 , b = 0 ; auto f1 = []{return a;}; // error,沒有捕獲外部變量 auto f2 = [&]{return a++;}; //ok,捕獲所有外部變量,并對a變量自加 auto f3 = [=]{return a;}; //ok,捕獲所有外部變量,并返回a auto f4 = [=]{return a++;}; //error,a變量是以復制方式捕獲的,不能修改 auto f5 = [a]{return a+b;}; //error,沒有捕獲b變量 auto f6 = [a,&b]{return a+ (b++);}; //ok,捕獲a以及b的引用,對b進行自加 auto f7 = [=,&b]{return a+ (b++);}; //ok, 捕獲所有外部變量和b的引用,對b進行自加
需要注意的是,lambda無法修改按值捕獲的外部變量,如果需要修改外部變量,可以通過引用方式捕獲。
關于lambda表達式的延遲調(diào)用很容易出錯,如下:
int a = 0; auto f = [=]{return a;}; a += 1; cout << f() << endl;
以上示例中,lambda按值捕獲了所有外部變量,在捕獲的時候 a的值就已經(jīng)被復制到 f 中了,之后a被修改,但是f里面存儲的a仍然是捕獲時的值,所以最終輸出的是 0.
如果希望lambda表達式在調(diào)用的時候能夠訪問外部變量,需要使用引用方式捕獲。
所以簡單來說,按值捕獲,外部變量會被復制一份存儲在lambda表達式變量中。
如果是按值捕獲并且又想修改外部變量,可以顯示指明lambda表達式為mutable:
int a = 0; auto f1 = [=]{return a++;}; //error,修改按值捕獲的外部變量 auto f2 = [=]() mutable {return a++;}; //ok
被mutable修飾的lambda表達式就算沒有參數(shù)也要寫明參數(shù)列表。
lambda表達式類型
lambda表達式的類型在C++11中被稱為“閉包類型”,它是一個特殊的,匿名的非nunion的類型。
可以認為它是帶有一個operator()的類,即仿函數(shù)。
我們可以通過std::function和std::bind來存儲和操作lambda表達式:
std::function<int(int)> f1 = [](int a){return a;}; std::function<int(void)> f2 = std::bind([](int a){return a;},123);
另外,對于沒有捕獲任何變量的lambda表達式,還可以被轉換成一個普通的函數(shù)指針:
using func_t = int(*)(int); func_t f = [](int a){return a;}; f(123);
lambda可以說是就地定義仿函數(shù)閉包的“語法 糖”。它的捕獲列表捕獲住任何外部變量,最終都會變?yōu)殚]包類型的成員變量。而一個成員變量的類的operator(),如果能直接被轉換為普通的函數(shù)指針,那么lambda表達式本身的this指針就丟掉了。而沒有捕獲任何外部變量的lambda表達式則不存在這個問題。
需要注意的是,沒有捕獲變量的lambda表達式可以直接轉換為函數(shù)指針,而捕獲變量的lambda表達式則不能轉換為函數(shù)指針。如下:
typedef void(*Ptr)(int*); Ptr p = [](int *p){delete p;}; //ok Ptr p1 = [&](int *p){delete p;}; //error
前面說到的按值捕獲無法修改捕獲的外部變量,因為按照C++標準,lambda表達式的operator()默認是const的,一個const成員函數(shù)是無法修改成員變量的值,而mutable的作用,就是取消operator()的const限制。
聲明式的編程風格
通過示例來看一下lambda的使用,在C++11之前,如果要用for_each函數(shù)將數(shù)組中的偶數(shù)數(shù)量打印出來,代碼如下:
#include <vector> #include <algorithm> class Count { public: ? ? Count(int &val):num(val){} ? ? void operator()(int val){ ? ? ? ? if(!(val & 1)){ ? ? ? ? ? ? ++num; ? ? ? ? } ? ? } private: ? ? int # }; int main() { ? ? std::vector<int> v = {1,2,3,4,5,6,7}; ? ? int count = 0; ? ? for_each(v.begin(),v.end(),Count(count)); ? ? std::cout << count << endl; ? ? return 0; }
如果使用lambda表達式,就可以簡化一下,真正使用閉包概念來替換這里的仿函數(shù)。
#include <vector> #include <algorithm> int main() { std::vector<int> v = {1,2,3,4,5,6,7}; int count = 0; for_each(v.begin(),v.end(),[&count](int val){ if(!(val & 1)){ ++count; } }); std::cout << count << endl; return 0; }
lambda表達式的價值在于,就地封裝短小的功能閉包,方便地表達出我們希望執(zhí)行的具體操作,并讓上下文結合的更加緊,代碼更加簡潔,更靈活,也提高了開發(fā)效率及可維護性。
參考:《深入應用C++11》
總結
到此這篇關于C++11 lambda(匿名函數(shù))表達式詳細介紹的文章就介紹到這了,更多相關C++11 lambda表達式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
C/C++實現(xiàn)動態(tài)數(shù)組的示例詳解
動態(tài)數(shù)組相比于靜態(tài)數(shù)組具有更大的靈活性,因為其大小可以在運行時根據(jù)程序的需要動態(tài)地進行分配和調(diào)整,本文為大家介紹了C++實現(xiàn)動態(tài)數(shù)組的方法,需要的可以參考下2023-08-08C++使用new和delete進行動態(tài)內(nèi)存分配與數(shù)組封裝
這篇文章主要介紹了C++使用new和delete進行動態(tài)內(nèi)存分配與數(shù)組封裝,運行期間才能確定所需內(nèi)存大小,此時應該使用new申請內(nèi)存,下面我們就進入文章學習具體的操作方法,需要的小伙伴可以參考一下2022-03-03