為什么要lambda函數
匿名函數是許多編程語言都支持的概念,有函數體,沒有函數名。1958年,lisp首先采用匿名函數,匿名函數最常用的是作為回調函數的值。正因為有這樣的需求,c++引入了lambda 函數,你可以在你的源碼中內聯一個lambda函數,這就使得創建快速的,一次性的函數變得簡單了。例如,你可以把lambda函數可在參數中傳遞給std::sort函數。
#include "stdafx.h" #include <algorithm> //標准模板庫算法庫 #include <cmath> //數學庫 #include <iostream> using namespace std; //絕對值排序 void abssort(float* x, unsigned n) { //模板庫排序函數 std::sort(x, x + n, // Lambda 開始位置 [](float a, float b) { return (std::abs(a) < std::abs(b)); } // lambda表達式結束 ); } int _tmain(int argc, _TCHAR* argv[]) { float a[5] = { 2.1f, 3.5f, 4.0f, 5.2f, 3.3f }; abssort(a, 5); for (auto& x : a) { cout << x << endl; } system("pause"); return 0; }
lambda函數的語法
基本形式如下:
[capture](parameters)->return-type {body}
- []叫做捕獲說明符,表示一個lambda表達式的開始。接下來是參數列表,即這個匿名的lambda函數的參數。
- parameters,普通參數列表
- ->return-type表示返回類型,如果沒有返回類型,則可以省略這部分。這涉及到c++11的另一特性,參見自動類型推導,最后就是函數體部分。
- capture clause(捕獲)
- lambda-parameter-declaration-list (變量列表)
- mutable-specification (捕獲的變量可否修改)
- exception-specification (異常設定)
- lambda-return-type-clause (返回類型)
- compound-statement (函數體)
外部變量的捕獲規則
默認情況下,即捕獲字段為 [] 時,lambda表達式是不能訪問任何外部變量的,即表達式的函數體內無法訪問當前作用域下的變量。
如果要設定表達式能夠訪問外部變量,可以在 [] 內寫入 & 或者 = 加上變量名,其中 & 表示按引用訪問,= 表示按值訪問,變量之間用逗號分隔,比如 [=factor, &total] 表示按值訪問變量 factor,而按引用訪問 total。
不加變量名時表示設置默認捕獲字段,外部變量將按照默認字段獲取,后面在書寫變量名時不加符號表示按默認字段設置,比如下面三條字段都是同一含義:
[&total, factor]
[&, factor]
[=, &total]
#include <functional> #include <iostream> using namespace std;int _tmain(int argc, _TCHAR* argv[]) { //lambd函數對象 auto fl = [](int x, int y){return x + y; }; cout << fl(2, 3) << endl; function<int(int, int)>f2 = [](int x, int y){return x + y; }; cout << f2(3, 4) << endl; system("pause"); return 0; }
不能訪問任何局部變量
如何傳進去局部變量
int test = 100; //lambd函數對象捕獲局部變量 auto fl = [test](int x, int y){return test +x + y; }; cout << fl(2, 3) << endl;
默認訪問所有局部變量
int test = 100; //lambd函數對象捕獲所有局部變量 auto fl = [=](int x, int y){return test +x + y; }; cout << fl(2, 3) << endl;
在C++11中這一部分被成為捕獲外部變量
捕獲外部變量
[captures] (params) mutable-> type{...} //lambda 表達式的完整形式
在 lambda 表達式引出操作符[ ]里的“captures”稱為“捕獲列表”,可以捕獲表達式外部作用域的變量,在函數體內部直接使用,這是與普通函數或函數對象最大的不同(C++里的包閉必須顯示指定捕獲,而lua語言里的則是默認直接捕獲所有外部變量。)
捕獲列表里可以有多個捕獲選項,以逗號分隔,使用了略微“新奇”的語法,規則如下
- [ ] :無捕獲,函數體內不能訪問任何外部變量
- [ =] :以值(拷貝)的方式捕獲所有外部變量,函數體內可以訪問,但是不能修改。
- [ &] :以引用的方式捕獲所有外部變量,函數體內可以訪問並修改(需要當心無效的引用);
- [ var] :以值(拷貝)的方式捕獲某個外部變量,函數體可以訪問但不能修改。
- [ &var] :以引用的方式獲取某個外部變量,函數體可以訪問並修改
- [ this] :捕獲this指針,可以訪問類的成員變量和函數,
- [ =,&var] :引用捕獲變量var,其他外部變量使用值捕獲。
- [ &,var]:只捕獲變量var,其他外部變量使用引用捕獲。
下面代碼示范了這些捕獲列表的用法:、
int x = 0,y=0;
- auto f1 = [=](){ return x; }; //以值方式捕獲使用變量,不能修改
- auto f2 = [&](){ return ++x; }; //以引用方式捕獲所有變量,可以修改,但要當心引用無效
- auto f3 = [x](){ return x; }; //以值方式捕獲x,不能修改
- auto f4 = [x,&y](){ y += x; }; //以值方式捕獲x,以引用方式捕獲y,y可以修改
- auto f5 = [&,y](){ x += y;}; //以引用方式捕獲y之外所有變量,y不能修改
- auto f6 = [&](){ y += ++x;}; //以引用方式捕獲所有變量,可以修改
- auto f7 = [](){ return x ;}; //無捕獲,不能使用外部變量,編譯錯誤
值得注意的是變化的捕獲發生在了lambda表達式的聲明之時,如果使用值方式捕獲,即使之后變量的值發生變化,lambda表達式也不會感知,仍然使用最初的值。如果想要使用外部變量的最新值就必須使用引用的捕獲方式,但也需要當心變量的生命周期,防止引用失效。
剛才的lambda表達式運行結果是:
- f1(); //以值方式捕獲,x,y不發生變化
- f2(); //函數內部x值為0,之后變為1,y沒有被修改,值仍然是0;
- f3(); //函數內部x值仍然為0,即f3()==0;
- f4(); //x,y均是0,運算后y仍然是0;
- f5(); //y是0;引用捕獲的x是1,運算后x仍然為1;
- f6(); //x,y均引用捕獲,運算后x,y均是2