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.