C++ lambda表示式
lambda表示式簡介
lambda表示式是C++11新特性(C++11 特性),用於建立一個可呼叫單元,可理解成匿名行內函數。
可呼叫單元 是指什麼?
可呼叫單元通常是指可呼叫物件(或函式)。
可呼叫物件
一個物件或表示式,如果可對其使用可呼叫運算子("()"),則稱這個物件或表示式為可呼叫物件。
可呼叫物件組成
4種:函式, 函式指標, 過載了"()"的類,lambda表示式。
本文主要介紹lambda表示式。
[======]
lambda表示式語法
格式
[捕獲列表](引數列表) mutable或exception -> 返回值型別 { 函式體 }
捕獲列表
捕獲列表可以為空,但"[]"不可用省略,用來標識一個lambda表示式開始。
lambda要訪問所在函式的區域性變數時,必須先捕獲。2種區域性變數捕獲方式:
- 值捕獲:建立lambda表示式時,拷貝所在函式的區域性變數的值。
- 引用捕獲:建立lambda表示式時,獲取變數的引用。需要確保lambda使用變數時,變數對應物件存在。
注意:值捕獲是發生在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 }
隱式捕獲
捕獲列表中寫“&”,告訴編譯器採用引用捕獲方式;寫“=”,告訴編譯器採用值捕獲方式。如果有個別局部變數不需要用統一的捕獲方式,可以專門指出其捕獲方式,然後用逗號方式分隔不同捕獲。
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;
});