閱讀g2log時,發現有兩行代碼竟然看不懂。ios
1. auto bg_call = [this, log_directory]() {return pimpl_->backgroundChangeLogFile(log_directory);};express
2. auto bg_call = [&]() {return pimpl_->backgroundFileName();};閉包
https://zh.wikipedia.org/wiki/%E5%8C%BF%E5%90%8D%E5%87%BD%E6%95%B0#C.2B.2B 中有所描述函數
C++ 98/03標準並不原生支持匿名函數。不過能夠利用Boost庫的Boost.Lambda來實現一個匿名函數。 this
C++11標準提供了匿名函數的支持,在《ISO/IEC 14882:2011》(C++11標準文檔)中叫作lambda表達式。一個lambda表達式有以下的形式:指針
[capture] (parameters) mutable exception attribute -> return_type { body }
必須用方括號括起來的capture列表來開始一個lambda表達式的定義。 code
lambda函數的形參表比普通函數的形參表多了3條限制: 對象
若是lambda函數沒有形參且沒有mutable、exception或attribute聲明,那麼參數的空圓括號能夠省略。但若是須要給出mutable、exception或attribute聲明,那麼參數即便爲空,圓括號也不能省略。 ip
若是函數體只有一個return語句,或者返回值類型爲void,那麼返回值類型聲明能夠被省略: 文檔
[capture](parameters){body}
一個lambda函數的例子以下:
[](int x, int y) { return x + y; } // 從return語句中隱式獲得的返回值類型 [](int& x) { ++x; } // 沒有return語句 -> lambda函數的返回值為void []() { ++global_x; } // 沒有參數,僅僅是訪問一個全局變量 []{ ++global_x; } // 與前者相同,()能夠被省略
在上面的第一個例子中這個無名函數的返回值是decltype(x+y)
。若是lambda函數體的形式是return expression
,或者什麼也沒返回,或者全部返回語句用decltype
都能檢測到同一類型,那麼返回值類型能夠被省略。
返回值類型能夠顯式指定,以下所示:
[](int x, int y) -> int { int z = x + y; return z; }
在這個例子中,一個臨時變量,z
,被建立來存儲中間過程。與通常的函數同樣,中間值在調用的先後並不存在。什麼也沒有返回的lambda表達式無需顯式指定返回值,沒有必要寫-> void
代碼。
lambda函數能夠捕獲lambda函數外的具備automatic storage duration的變量,即函數的局部變量與函數形參變量。函數體與這些變量的集合合起來稱作閉包。這些外部變量在聲明lambda表達式時列在在方括號[
和]
中。空的方括號表示沒有外界變量被capture或者按照默認方式捕獲外界變量。這些變量被傳值捕獲或者引用捕獲。對於傳值捕獲的變量,默認爲只讀(這是因爲lambda表達式生成的爲一個函數對象,它的operator()
成員缺省有const屬性)。修改這些傳值捕獲變量將致使編譯報錯。但在lambda表達式的參數表的圓括號後面使用mutable關鍵字,就容許lambda函數體內的語句修改傳值捕獲變量,這些修改與lambda表達式(其實是用函數對象實現)有相同的生命期,但不影響被傳值捕獲的外部變量的值。lambda函數能夠直接使用具備static存儲期的變量。若是在lambda函數的捕獲列表中給出了static存儲期的變量,編譯時會給出警告,仍然按照lambda函數直接使用這些外部變量來處理。所以具備static存儲期的變量即便被聲明爲傳值捕獲,修改該變量實際上直接修改了這些外部變量。編譯器生成lambda函數對應的函數對象時,不會用函數對象的數據成員來保持被「捕獲」的static存儲期的變量。示例:
[] // 沒有定義任何變量,但必須列出空的方括號。在Lambda表達式中嘗試使用任何外部變量都會導致編譯錯誤。 [x, &y] // x是按值傳遞,y是按引用傳遞 [&] // 任何被使用到的外部變量都按引用傳入。 [=] // 任何被使用到的外部變量都按值傳入。 [&, x] // x按值傳入。其它變量按引用傳入。 [=, &z] // z按引用傳入。其它變量按值傳入。
下面這個例子展現了lambda表達式的使用:
std::vector<int> some_list{ 1, 2, 3, 4, 5 }; int total = 0; std::for_each(begin(some_list), end(some_list), [&total](int x) { total += x; } );
在類的非靜態成員函數中定義的lambda表達式能夠顯式或隱式捕捉指針,從而能夠引用所在類對象的數據成員與函數成員。this
lambda函數的函數體中,能夠訪問下述變量:
lambda函數的數據類型是函數對象,保存時必須用std::function
模板類型或auto
關鍵字。 例如:
#include <functional> #include <vector> #include <iostream> double eval(std::function <double(double)> f, double x = 2.0) { return f(x); } int main() { std::function<double(double)> f0 = [](double x){return 1;}; auto f1 = [](double x){return x;}; decltype(f0) fa[3] = {f0,f1,[](double x){return x*x;}}; std::vector<decltype(f0)> fv = {f0,f1}; fv.push_back ([](double x){return x*x;}); for(int i=0;i<fv.size();i++) std::cout << fv[i](2.0) << std::endl; for(int i=0;i<3;i++) std::cout << fa[i](2.0) << std::endl; for(auto &f : fv) std::cout << f(2.0) << std::endl; for(auto &f : fa) std::cout << f(2.0) << std::endl; std::cout << eval(f0) << std::endl; std::cout << eval(f1) << std::endl; std::cout << eval([](double x){return x*x;}) << std::endl; return 0; }
一個lambda函數的捕捉表達式爲空,則能夠用普通函數指針存儲或調用。例如:
auto a_lambda_func = [](int x) { /*...*/ }; void (* func_ptr)(int) = a_lambda_func; func_ptr(4); //calls the lambda.
C++14增長了初始捕獲(init capture)。因爲lambda表達式實際構造了一個函數對象,初始捕獲實質是初始化了函數對象的成員。C++14還容許lambda函數的形參使用auto
關鍵字做爲其類型,這實質上是函數對象的operator()
成員做爲模板函數;而且容許可變參數模板。
auto a_lambda_func = [data1=101](int x) { /*...*/ }; //初始捕獲,實質上是在函數對象增長了數據成員data1並初始化 auto ptr = std::make_unique<int>(10); //See below for std::make_unique auto lambda1 = [ptr = std::move(ptr)] {return *ptr;} //大體等效於: auto lambda2 = [ptr = std::make_unique<int>(10)] {return *ptr;} auto lambda3 = [](auto x, auto y) {return x + y;} //lambda函數的形參類型爲auto struct unnamed_lambda //這至關於函數對象: { template<typename T, typename U> auto operator()(T x, U y) const {return x + y;} }; auto lambda4 = [](auto&&... params) //可變參數的函數模板{ return (foo(std::forward<decltype(params)>(params)...)); }