C++ Primer 筆記——lambda表達式
1.一個lambda表達式表示一個可調用的代碼單元,可以理解為一個未命名的內聯函數,但是與函數不同,lambda表達式可能定義在函數內部。其形式如下:
[capture list] (parameter list) -> return type { function body }
- capture list 是一個lambda所在函數中定義的局部變量的列表(通常為空)
- return type, parameter list 和 function body與任何普通函數一樣,分別表示返回類型,參數列表和函數體
- lambda必須使用尾置返回
- 我們可以忽略參數列表和返回類型,但必須永遠包含捕獲列表和函數體
- 如果忽略返回類型,lambda會從函數體推斷出返回類型。
- 如果lambda的函數體包含任何單一return語句之外的內容,且未指定返回類型,則返回void
- 捕獲列表只用於局部非static變量,lambda可以直接使用局部static變量和在它所在函數之外聲明的名字。
void test() { int i = 1; int j = 2; auto f = [i, j](int base) -> int { return base + i + j; }; // i,j必須在捕獲列表裏面,這裏的返回類型int其實可以省略 intk = f(0); // k的結果為3 }
2.當定義一個lambda時,編譯器生成一個與lambda對應的新的(未命名的)類類型。默認情況下這個類都包含一個對於該lambda所捕獲的變量的數據成員。類似任何普通類的數據成員,lambda的數據成員也在lambda對象創建時被初始化。
3.與傳值參數類似,采用值捕獲的前提是變量可以拷貝,與參數不同,被捕獲的變量的值是在lambda創建時拷貝,而不是調用時拷貝。
void test() { int i = 10; auto f = [i] {return i + 1; }; // 此時i的值就已經被拷貝了int j = f(); }
4.當以引用方式捕獲一個變量時,必須保證在lambda執行時變量是存在的。
void test() { int i = 10; auto f = [&i] {return ++i; }; int j = f(); // 此時i等於11 }
5.在捕獲列表中寫一個 &或= 可以告訴編譯器我們想采用值捕獲還是引用捕獲,這種方法叫做隱式捕獲。
void test() { int i = 10; auto f = [&] {return ++i; }; // 采用引用捕獲 int j = f(); }
6.我們也可以混合使用值捕獲或者引用捕獲。當使用混合方式的時候,顯示捕獲的方式不可以與隱式捕獲的方式相同。
void test() { int i = 10; int j = 9; auto f = [=, &i] {i++; return i + j; }; // i采用引用捕獲,其他采用值捕獲 int k = f(); // k等於20 }
7.默認情況下,對於一個值被拷貝的變量,lambda不會改變其值,如果我們希望能改變一個被捕獲的變量的值,就必須在參數列表首加上mutable。
void test() { int i = 10; auto f = [i] (){return ++i; }; // 錯誤,不可以改變i的值 auto f1 = [i] () mutable {return ++i; }; // 正確 }
8.bind函數(頭文件 functional中)可以看作一個通用的函數適配器,它接受一個可調用對象,生成一個新的可調用對象來適應原對象的參數列表。調用bind的一般形式為:
auto newCallable = bind(callable, arg);
void test(int i, int j) { std::cout << i - j << std::endl; } auto test0 = std::bind(test, 5, 1); test0(); // 結果4 auto test1 = std::bind(test, std::placeholders::_1, 5, std::placeholders::_2, 1); // 錯誤,不可以這麽寫 test1(); auto test2 = std::bind(test, std::placeholders::_1, 5); test2(1); // 結果-4 auto test3 = std::bind(test, std::placeholders::_1, std::placeholders::_2); test3(3, 2); // 結果1 auto test4 = std::bind(test, std::placeholders::_2, std::placeholders::_1); // 參數順序可以重排 test4(3, 2); // 結果-1 auto test5 = std::bind(test, 1, std::placeholders::_1); // 註意這個占位符占的是test5()的參數位置,而不是test()的 test5(5); // 結果-4
以上代碼請註意命名空間,否則會和socket的bind函數產生二義性。
9.如果我們想綁定的參數無法拷貝,或者我們想引用時應該使用ref函數。
void test(int& i, int j) { std::cout << i - j << std::endl; } int i = 5; auto test0 = std::bind(test, std::ref(i), 1); test0(); // 結果4
C++ Primer 筆記——lambda表達式