1. 程式人生 > 實用技巧 >C++11中的匿名函式(lambda函式,lambda表示式)

C++11中的匿名函式(lambda函式,lambda表示式)

這篇文章是根據維基百科整理來的,原文請看:http://en.wikipedia.org/wiki/Anonymous_function#C.2B.2B

  C++11提供了對匿名函式的支援,稱為Lambda函式(也叫Lambda表示式). Lambda表示式具體形式如下:

    [capture](parameters)->return-type{body}

  如果沒有引數,空的圓括號()可以省略.返回值也可以省略,如果函式體只由一條return語句組成或返回型別為void的話.形如:

    [capture](parameters){body}

  下面舉了幾個Lambda函式的例子:    

[](int x, int y) { return x + y; } // 隱式返回型別
[](int& x) { ++x; }   // 沒有return語句 -> lambda 函式的返回型別是'void'
[]() { ++global_x; }  // 沒有引數,僅訪問某個全域性變數
[]{ ++global_x; }     // 與上一個相同,省略了()

  可以像下面這樣顯示指定返回型別:

[](int x, int y) -> int { int z = x + y; return z; }

  在這個例子中建立了一個臨時變數z來儲存中間值. 和普通函式一樣,這個中間值不會儲存到下次呼叫. 什麼也不返回的Lambda函式可以省略返回型別, 而不需要使用 -> void 形式.
  Lambda函式可以引用在它之外宣告的變數. 這些變數的集合叫做一個閉包. 閉包被定義在Lambda表示式宣告中的方括號[]內. 這個機制允許這些變數被按值或按引用捕獲.下面這些例子就是:

[]        //未定義變數.試圖在Lambda內使用任何外部變數都是錯誤的.
[x, &y]   //x 按值捕獲, y 按引用捕獲.
[&]       //用到的任何外部變數都隱式按引用捕獲
[=]       //用到的任何外部變數都隱式按值捕獲
[&, x]    //x顯式地按值捕獲. 其它變數按引用捕獲
[=, &z]   //z按引用捕獲. 其它變數按值捕獲

  接下來的兩個例子演示了Lambda表示式的用法.

std::vector<int> some_list;
int total = 0;
for (int i=0;i<5;++i) some_list.push_back(i);
std::for_each(begin(some_list), end(some_list), [&total](int x) 
{
    total += x;
});

  此例計算list中所有元素的總和. 變數total被存為lambda函式閉包的一部分. 因為它是棧變數(區域性變數)total的引用,所以可以改變它的值.

std::vector<int> some_list;
  int total = 0;
  int value = 5;
  std::for_each(begin(some_list), end(some_list), [&, value, this](int x) 
  {
    total += x * value * this->some_func();
  });

  此例中total會存為引用, value則會存一份值拷貝. 對this的捕獲比較特殊, 它只能按值捕獲. this只有當包含它的最靠近它的函式不是靜態成員函式時才能被捕獲.對protect和priviate成員來說, 這個lambda函式與建立它的成員函式有相同的訪問控制. 如果this被捕獲了,不管是顯式還隱式的,那麼它的類的作用域對Lambda函式就是可見的. 訪問this的成員不必使用this->語法,可以直接訪問.
  不同編譯器的具體實現可以有所不同,但期望的結果是:按引用捕獲的任何變數,lambda函式實際儲存的應該是這些變數在建立這個lambda函式的函式的棧指標,而不是lambda函式本身棧變數的引用. 不管怎樣, 因為大數lambda函式都很小且在區域性作用中, 與候選的行內函數很類似, 所以按引用捕獲的那些變數不需要額外的儲存空間.
  如果一個閉包含有區域性變數的引用,在超出建立它的作用域之外的地方被使用的話,這種行為是未定義的!
  lambda函式是一個依賴於實現的函式物件型別,這個型別的名字只有編譯器知道. 如果使用者想把lambda函式做為一個引數來傳遞, 那麼形參的型別必須是模板型別或者必須能建立一個std::function類似的物件去捕獲lambda函式.使用 auto關鍵字可以幫助儲存lambda函式,

auto my_lambda_func = [&](int x) { /*...*/ };
auto my_onheap_lambda_func = new auto([=](int x) { /*...*/ });

  這裡有一個例子, 把匿名函式儲存在變數,陣列或vector中,並把它們當做命名引數來傳遞

#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) << "\n";
     for(int i=0;i<3;i++)          std::cout << fa[i](2.0) << "\n";
     for(auto &f : fv)             std::cout << f(2.0) << "\n";
     for(auto &f : fa)             std::cout << f(2.0) << "\n";
     std::cout << eval(f0) << "\n";
     std::cout << eval(f1) << "\n";
     return 0;
}

  一個沒有指定任何捕獲的lambda函式,可以顯式轉換成一個具有相同宣告形式函式指標.所以,像下面這樣做是合法的:

auto a_lambda_func = [](int x) { /*...*/ };
void(*func_ptr)(int) = a_lambda_func;
func_ptr(4); //calls the lambda.