一、定義
lambda是一種匿名函數,即沒有函數名的函數。lambda函數的語法定義爲:
[捕捉列表] (參數) mutable -> 返回值類型 {函數體}
二、[捕捉列表],定義能夠捕獲的函數外的變量,通俗地說捕獲就是匿名函數外所在作用域內的變量,這些變量可以在函數體內部調用。捕獲有兩種形式,按值捕獲和按引用捕獲。按值捕獲的變量相當於和函數外作用域內同名的一個變量,在函數內對改變量的修改不會影響函數外變量。按引用捕獲就是直接引用函數外作用域內的變量,在函數內對改變量的修改就是對函數外變量的修改。
[var] 表示以值傳遞方式捕捉變量var
[=] 表示值傳遞捕捉所有父作用域變量
[&var] 表示以引用傳遞方式捕捉變量var
[&] 表示引用傳遞捕捉所有父作用域變量
[this] 表示值傳遞方式捕捉當前的this指針
[=,&a] 表示以引用傳遞方式捕捉a,值傳遞方式捕捉其他變量
[=a,&] 表示以值傳遞方式捕捉a,引用傳遞方式捕捉其他變量
示例:
int main(int argc, char* argv[])
{
int a = 5, b = 7;
auto total = [](int x, int y)->int {return x + y; }; //接受兩個參數
cout << total(a, b)<<endl; //12
auto fun1 = [=] {return a + b; }; //值傳遞捕捉父作用域變量
cout << fun1() << endl; //12
auto fun2 = [&](int c) {b = a + c; a = 1; }; //省略了返回值類型,引用捕獲所有
fun2(3); //1 8
cout << a <<" "<< b << endl;
a = 5; b = 7; //被修改後,重新賦值
auto fun3 = [=, &b](int c) mutable {b = a + c; a = 1; }; //以值傳遞捕捉的變量,在函數體裏如果要修改,要加mutaple,因爲默認const修飾
fun3(3);
cout << a << " " <<b<< endl; //5,8
a = 5; b = 7; //被修改後,重新賦值
auto fun4 = [=](int x, int y) mutable->int {a += x; b += y; return a + b; };
int t = fun4(10, 20);
cout << t << endl; //42
cout << a <<" "<< b << endl; //5 7
return 0;
}
三、mutable
匿名函數默認是const屬性,值傳遞的變量在函數體內不能被修改(引用傳遞的變量可以修改)。如果需要在函數體內去修改值傳遞的變量,則需要添加mutable關鍵字。當lambda中使用了mutable修飾符,則“參數列表”是不可以省略掉的,即使參數爲空。
四、返回值類型
函數體中所有return返回的都是同一類型的話,編譯器會自行判斷函數的返回類型,“-> 返回值類型”可以省略。
五、lambda和static inline函數
Lambda函數可以省略外部聲明的static inline函數,其相當於一個局部函數。局部函數僅屬於父作用域,比起外部的static inline函數,或者是自定義的宏,Lambda函數並沒有實際運行時的性能優勢(但也不會差),但是Lambda函數可讀性更好。父函數結束後,該Lambda函數就不再可用了,不會污染任何名字空間。
六、lambda函數與函數指針
Lambda函數並不是簡單的函數指針類型,或者自定義類型;每個Lambda函數會產生一個閉包類型的臨時對象(右值)。但是C++11允許Lambda函數向函數指針的轉換,前提是:
(1)Lambda沒有捕捉任何變量
(2)函數指針所示的函數原型,必須和Lambda有相同的調用方式
示例:
int main(int argc, char* argv[])
{
int a = 3, b = 4;
auto total = [](int x, int y)->int {return x + y; };
typedef int(*all)(int x, int y);
typedef int(*one)(int x);
all p;
p = total;
one q;
q = total; //報錯,參數不一致
decltype(total) all_1 = total;
decltype(total) all_2 = p; //報錯,指針無法轉換爲Lambda
return 0;
}
七、lambda與STL
從C++11開始,Lambda被廣泛用在STL中,比如foreach。與函數指針比起來,函數指針有巨大的缺陷:1.函數定義在別處,閱讀起來很困難;2.使用函數指針,很可能導致編譯器不對其進行inline優化,循環次數太多時,函數指針和Lambda比起來性能差距太大。函數2指針不能應用在一些運行時才能決定的狀態,在沒有C++11時,只能用仿函數。使得學習STL算法的代價大大降低。
但是Lambda並不是仿函數的完全代替者。由Lambda的捕捉列表的限制造成的,僅能捕捉副作用域的變量。放函數具有天生跨作用域共享的特徵。
參考: