匿名函數 lambda表達式(lambda expression)

閱讀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

C++ 98/03標準並不原生支持匿名函數。不過能夠利用Boost庫的Boost.Lambda來實現一個匿名函數。 this

C++ 11

C++11標準提供了匿名函數的支持,在《ISO/IEC 14882:2011》(C++11標準文檔)中叫作lambda表達式。一個lambda表達式有以下的形式:指針

[capture] (parameters) mutable exception attribute -> return_type { body }

必須用方括號括起來的capture列表來開始一個lambda表達式的定義。 code

lambda函數的形參表比普通函數的形參表多了3條限制: 對象

  1. 參數不能有缺省值
  2. 不能有可變長參數列表
  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表達式聲明在類中),須要注意的是實際上捕獲了對象的this指針。
  • 具備靜態存儲期的變量(如全局變量)
  • 被捕獲的外部變量
    • 顯式捕獲的變量
    • 隱式捕獲的變量,使用默認捕獲模式(傳值或引用)來訪問。

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

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)...));
}
相關文章
相關標籤/搜索