lambda表達式簡介
lambda表達式是C++11新特性(C++11 特性),用於創建一個可調用單元,可理解成匿名內聯函數。
可調用單元 是指什么?
可調用單元通常是指可調用對象(或函數)。
可調用對象
一個對象或表達式,如果可對其使用可調用運算符("()"),則稱這個對象或表達式為可調用對象。
可調用對象組成
4種:函數, 函數指針, 重載了"()"的類,lambda表達式。
本文主要介紹lambda表達式。
[======]
lambda表達式語法
格式
[捕獲列表](參數列表) mutable或exception -> 返回值類型 { 函數體 }
捕獲列表
捕獲列表可以為空,但"[]"不可用省略,用來標識一個lambda表達式開始。
lambda要訪問所在函數的局部變量時,必須先捕獲。2種局部變量捕獲方式:
- 值捕獲:創建lambda表達式時,拷貝所在函數的局部變量的值。
- 引用捕獲:創建lambda表達式時,獲取變量的引用。需要確保lambda使用變量時,變量對應對象存在。
注意:
1)值捕獲是發生在lambda創建時,而非調用時。
2)lambda無法捕獲任何static變量、全局變量,不過可以在lambda表達式內部直接訪問。
// 值捕獲拷貝變量,發生在lambda創建時
void func1()
{
size_t v1 = 10;
auto a = [v1]()->int { return v1; }; // 值捕獲v1 = 10
v1 = 0;
auto b = a(); // lambda調用時,不會改變捕獲值,因此b為10
}
// 引用捕獲獲取變量引用
void func2()
{
size_t v1 = 10;
auto a = [&v1]()->int { return v1; }; // 引用捕獲v1
v1 = 0;
auto b = a(); // b為0
}
// lambda訪問全局變量、static變量
size_t sz = 10;
static int counter = 0;
void func3()
{
auto f0 = [sz](int a, int b) { return a > b}; // 錯誤:lambda無法捕獲任何具有靜態存儲持續時間的變量
auto f = [](string& x, string& y)->bool {
counter++; // OK:lambda表達式可以直接訪問static變量
if (x.size() > sz) { // OK:lambda表達式可以直接訪問global變量
return true;
}
return x > y;
};
string s1 = "123456";
string s2 = "abcdefgh";
auto r = f(s1, s2);
cout << r << endl; // 打印0
cout << counter << endl; // 打印1
}
隱式捕獲
捕獲列表中寫“&”,告訴編譯器采用引用捕獲方式;寫“=”,告訴編譯器采用值捕獲方式。如果有個別局部變量不需要用統一的捕獲方式,可以專門指出其捕獲方式,然后用逗號方式分隔不同捕獲。
string v1 = "abc";
string v2 = "123465";
string v3 = "qwert";
auto f1 = [&](const string& s)->{ cout << v1.size() + s.size() << endl; }; // 引用捕獲
auto f2 = [=](const string& s)->{ cout << v2.size() + s.size() << endl; }; // 值捕獲
auto f3 = [=, &v1](const string& s)->{ cout << v2.size() + s.size() << endl; }; // 引用捕獲v1, 其余都是值捕獲
auto f4 = [&, v2, v3](const string& s)->{ cout << v2.size() + s.size() << endl; }; // 值捕獲v2, v3, 其余都是引用捕獲
f1("test1");
f2("test2");
f3("test3");
參數列表
參數列表 可以為空,但"()"不可用省略。類似於函數定義。
mutable關鍵字
-
mutable對於const函數作用
const成員函數中,通常不能修改non-static數據成員。如果要修改,需要將數據成員聲明為mutable,表示該變量可變,不再有constness(常量性)約束。
具體可參見Effective C++ 條款03:盡可能使用const。 -
mutable對於lambda作用
在lambda表達式中,mutable有類似效果,默認不能修改值捕獲的變量。
當lambda要修改值捕獲的變量時,必須添加mutable聲明。
void fcn()
{
size_t v1 = 10;
auto f = [v1]() mutable { return ++v1; }; // 加上mutable才能修改值捕獲的v1
// <=> auto f = [v1]() mutable -> int { return ++v1; };
v1 = 0;
auto j = f(); // j 為11
}
指定lambda返回類型
默認情況下,
1)如果lambda函數體只包含單一return語句,可以省略lambda返回類型(編譯器自動推斷返回類型)。
2)如果包含return之外的任何語句,編譯器假定此lambda返回void。
簡單來說,如果編譯器無法推斷lambda返回類型,就需要尾置返回類型,不可省略。
例,
vector<int> vec;
... // vec插入數據
// OK, 單一return語句, 編譯器能推斷出lambda的返回類型
transform(vec.begin(), vec.end(), vec.begin(), [](int i) { return i < 0 ? -i : i; });
// 錯誤, 不能推斷lambda的返回類型
transform(vec.begin(), vec.end(), vec.begin(), [](int i) -> int {
/* 非單一return語句 */
if (i < 0) return -i;
else return i;
});
