lambda表達式又稱匿名函數(Anonymous function),其構造了一個能夠在其做用範圍內捕獲變量的函數對象。html
lambda表達式實際爲一個仿函數functor,編譯器後會生成一個匿名類(注:這個類重載了()運算符)c++
與普通函數指針相比,Lambda表達式能夠包含數據成員,也就是說它是能夠有狀態的。下面舉一個簡單的例子說明一下:閉包
int sum = 0; std::vector<int> arry = { 1,2,3,4,5 }; std::for_each(begin(arry), end(arry), [&sum](int x){sum += x;});
上述代碼被編譯器展開後,會變成:函數
struct lambda_b53d8cae67476f0e5f04d9defa3a2e2b { private: int* m_pSum; public: lambda_b53d8cae67476f0e5f04d9defa3a2e2b(int* pSum) { m_pSum = pSum; } void operator()(int x) const { *m_pSum += x; } }; int sum = 0; std::vector<int> arry = { 1,2,3,4,5 }; lambda_b53d8cae67476f0e5f04d9defa3a2e2b obj(&sum); std::for_each(begin(arry), end(arry), obj);
lambda表示式的結構this
從C++11起,開始提供了匿名函數的支持,一個lambda表達式形如:spa
[capture] (parameters) specifiers -> return_type { body }3d
capture指針
捕獲的外部變量列表,經過逗號分隔,可進行傳值捕獲或者引用捕獲,lambda表達式與這些捕獲的外部變量會構成一個閉包(Closure),外部變量爲閉包的成員變量code
int g_Value = 0; class CLambda { protected: int m_Value; public: void Test1(int InValue) { int Value = 0; auto a1 = [](int x) {/*僅能訪問全局外部變量*/}; auto a2 = [Value](int x) {/*值傳遞局部變量Value*/}; auto a3 = [this](int x) {/*值傳遞this指針*/}; auto a4 = [&Value](int x) {/*引用傳遞局部變量Value*/}; auto a5 = [=](int x) {/*值傳遞全部可訪問的外部變量*/}; auto a6 = [&](int x) {/*引用傳遞全部可訪問的外部變量*/}; auto a7 = [=, &Value](int x) {/*引用傳遞局部變量Value,值傳遞全部其餘可訪問的外部變量*/}; auto a8 = [&, Value](int x) {/*值傳遞局部變量Value,引用傳遞全部其餘可訪問的外部變量*/}; } };
① 全局變量、靜態變量不用顯示寫在外部變量列表中,可直接在lambda表達式中讀寫htm
② 傳值捕獲的外部變量是一個副本,默認狀況下在lambda表達式中是隻讀的,若想修改該副本,須要在lambda表達式上添加mutable關鍵字 如:auto a2 = [Value](int x) mutable {Value++;};
---------------------------------------------------------------------------
在C++中,mutable是爲了突破const的限制而設置的,用來修飾類的非靜態、很是量的數據成員,讓被修飾的變量永遠處於可變的狀態
mutable的做用有兩點:
a. 保持常量對象中大部分數據成員仍然是「只讀」的狀況下,實現對個別數據成員的修改;
b. 使類的const函數能夠修改其mutable數據成員。
---------------------------------------------------------------------------
③ 在類的非靜態成員函數中定義的lambda表達式能夠經過捕捉this指針,來訪問對象的成員變量和成員函數
④ c++14中增長廣義捕獲(Generalized capture):即在捕獲子句中增長並初始化新的變量,該變量不須要在lambda表達式所處的閉包域中存在;
即便在閉包域中存在也會被新變量覆蓋。新變量類型由它的初始化表達式推導獲得。一個用途是能夠從閉包域中捕獲只供移動的變量並使用它。
int Value = 0; auto a1 = [Value = 100] { return Value; };// 捕獲子句中定義的Value變量會覆蓋同名的外部局部變量 int result = a1(); // result=100 std::vector<int> Vout = { 10, 20, 30, 40, 50 }; auto a2 = [Vin = std::move(Vout)]{ // 將外部局部變量Vout移動到Vin變量,避免發生拷貝耗時操做 int sum = 0; for (auto n : Vin) { sum += n; } return sum; }; int result2 = a2(); // result2=150
parameters
lambda表達式本身的參數列表
1. 若lambda函數沒有形參且沒有被mutable等修飾,則參數的空圓括號能夠省略
如:auto a = []{ ++g_Value; };
2. c++14支持auto類型的形參
auto a = [](auto x, auto y) {return x + y;}; //至關於定義了模板類型()運算符的函數對象 struct unnamed_lambda { template<typename T, typename U>
auto operator()(T x, U y) const {return x + y;} } a;
3. c++14支持可變參數模板
int foo(int n1, int n2, bool flag) { return flag ? n1 : n2; } auto a = [](auto&&... params) //可變參數的模板 { return (foo(std::forward<decltype(params)>(params)...)); }; int result = a(1, 2, true); //result=1
4. 與普通函數相比,lambda表達式的參數有以下限制
① 參數不能有缺省值 如:int Add1(int a, int b=10)
② 不能有可變長參數列表 如:int Add2(int count, ...)
③ 不能有無名參數 如:int Add3(int a, int b, int) // 第三個參數爲無名參數,用於之後擴展
specifiers
說明符,可選。如上文中的mutable
return_type
返回類型
在如下狀況下,可省略return_type
① 返回值爲void類型 如:auto a = [](int x) {x = 100;}; //返回值類型爲void
② 全部返回語句用decltype都能檢測到同一類型 如:auto a = [](int x, float y) { if (x > 0) return x + y; return y; }; //返回值類型推導爲float
使用lambda表達式
lambda表達式實際爲一個函數對象,在使用時要注意lambda表達式以及被其捕獲的外部變量的生命週期
把匿名函數存儲在變量,當作有名函數來使用
lambda表達式的數據類型是函數對象,可用auto類型、std::function模板類型(需#include <functional>)進行保存和調用
當捕獲外部變量列表爲空時,也可用普通函數指針進行保存和調用
int Value = 0; auto a1 = [&Value](int x) {Value = x;}; std::function<float(int,float)> a2 = [Value](int x, float y) { return x + y; }; //須要#include <functional> a1(100); a2(100, 200.0f); // lambda函數的捕獲外部變量列表爲空時,可以使用普通函數指針來保存 int(*func1_ptr)(int) = [](int x) {return x; }; bool(*func2_ptr)(int, int) = [](int x, int y) { return x > y; }; int n = func1_ptr(1); bool b = func2_ptr(1, 2);
懸掛引用問題
lambda表達式的閉包含有局部變量的引用(懸掛引用 Dangling references),在超出建立它的做用域以外的地方被使用的話,將引起內存越界訪問
std::function<int()> a; { int Value = 0; a = [&Value] {return Value; }; } a(); // 局部變量Value超過做用域,已被回收,調用lambda表達式將產生內存越界訪問
參考