從c++11開始,可以使用lambda表達式,介紹一下使用方法以及事項, 你可以把它當作一個無名重載了operator()運算符的類對象。
知識背景
尾置返回類型
C++11標准中,引入定義函數時的一種新的方法,使用尾置返回類型.這種形式對於返回類型比較復雜的情況很有效。
通常情況下,我們定義或聲明一個函數時,是這樣的:
int add(int a, int b);
尾置返回類型的定義是這樣的:
auto add(int a, int b) -> int;
當我們定義一個返回指向10個元素的int數組指針的函數時,按正常形式是這樣的:
int (*func())[10];
當我們使用尾置返回類型時,它是這樣的:
auto func() -> int(*)[10];
lambda表達式
定義格式
lambda采用尾置返回類型,它完整的const聲明形式為: [] (參數列表) -> 返回值類型 {函數體}
, const是指在捕獲列表內通過值捕獲的參數在lambda內部是不可以改變的。
int func(vector<int> array)
{
auto func = [](int a, int b) ->bool { return a < b;}
std::sort(array.begin(), array.end(), func);
}
void sample ()
{
int a = 10;
auto func1 = [a] () {++a;}; // 編譯錯誤, 變量a的類型在lambda體內為const的。
auto func2 = [a] () mutable {++a;}; // 沒有問題
auto func3 = [&a] () {++a;}; // 沒有問題
}
參數列表與返回值類型可以根據是否需要,進行省略掉, 規則如下:
- 當定義的lambda表達式的參數為空時,可以省略掉:
[] -> 返回值類型 {函數體 }
- 當定義的lambda表達式的函數體僅有一條
return ****
的語句時,返回值類型也可以省略,編譯器會根據返回值的類型自動推斷。 但是如果函數體是多條語句而你省略了返回值類型,編譯器認為返回類型是void. 例如下面的lambda表達式的返回值類型默認為void, 一看就不對啊,所以此時你別省略返回值類型:auto Addfunc = [] (int a, int b) { int c = a + b; return c;}
- 捕獲列表與函數體任何時候都不可以省略。
捕獲列表
捕獲列表只需要捕獲lambda所在作用域內常規的局部變量,對於非局部變量以及局部的靜態變量不需要捕獲,可以直接使用。 例如:
int g_value = 100;
void sample()
{
int value1 = 10;
static value2 = 100;
auto func = [value1]() {cout << g_value << value1 << value2; };
}
值捕獲
方式一: 使用 [=]
隱式捕獲lambda內所有使用到的變量的值。
方式二: 使用 [val1, val2, val3, ...]
顯示捕獲lambda內使用到的變量的值。
void sample()
{
int value1 = 10;
int value2 = 10;
int value3 = 10;
auto func1 = [=] () -> int {return value1 + value2 + value3;};
auto func2 = [value1, value2, value3] () {value1 + value2 + value3;};
}
引用捕獲
方式一: 使用 [&]
隱式捕獲lambda內所有使用到的變量的值。
方式二: 使用 [&val1, &val2, &val3, ...]
顯示捕獲lambda內使用到的變量的值。
void sample()
{
int value1 = 10;
int value2 = 10;
int value3 = 10;
auto func1 = [&] () -> int {return value1 + value2 + value3;};
auto func2 = [&value1, &value2, &value3] () {return value1 + value2 + value3;};
}
混合捕獲
方式一: 使用 [val1, &val2, val3, ...]
隨意的組合值捕獲和引用捕獲來獲取lambda內使用到的變量的值。
方式二: 使用 [=, &val1, &val2, ...]
表示除了手動指出來的變量通過引用捕獲之外,其它的變量都是通過值進行捕獲。
方式三: 使用 [&, val1, val2, ...]
表示除了手動指出來的變量通過值捕獲之外,其它的變量都是通過引用進行捕獲。
void sample()
{
int value1 = 10;
int value2 = 10;
int value3 = 10;
auto func1 = [value1, &value2, value3] () {return value1 + value2 + value3;};
auto func2 = [=, &value2] () {return value1 + value2 + value3;};
auto func3 = [&, value3] () {return value1 + value2 + value3;};
}
注意事項:
- 當lambda表達式定義在類內的成員函數時,如果在lambda表達式內部要訪問類的成員函數或成員變量(無論public/protected/private)時,要么顯示捕獲this指針,要么通過[=]或[&]進行隱式捕獲。
- 使用引用捕獲時,特別注意這些參數實體的生存期,保證調用lambda時這些實體是有意義的,避免懸垂引用的產生。
使用mutable關鍵字修飾的lambda
默認的lamba的聲明方式是const聲明,通過值獲取的參數在lambda內是無法修改的,如果要改變該值,在參數列表后面加上mutable關鍵字。
void sample ()
{
int a = 10;
auto func1 = [a] () {++a;}; // 編譯錯誤
auto func2 = [a] () mutable {++a;}; // 沒有問題
}
lambda表達式本身是純右值表達式(你不可能對它的結果取地址的), 它的類型獨有的無名非聯合非聚合類類型,被稱為閉包類型(closure type), 它可以有以下兩個成員函數:
ret-type operator() (參數列表) const {函數體} // 未使用關鍵字mutable時,默認情況
ret-type operator() (參數列表) {函數體} // 使用了mutable 關鍵字時
友情提示:
mutable 關鍵字只對值捕獲參數有影響,對引用捕獲的參數無影響。原因是:引用參數能否修改為參數本身是否為const類型決定。即使一個類的成員函數有 const 聲明(該const就是修改this指針的),照樣可以通過該成員函數修改一個引用類型的成員變量,例如:
class Test { public: Test(int& value) : a(value) {} void Increase() const {++a;} private: int& a; };