Lambda 表示式因數學中的 \(\lambda\) 演算得名, 直接對應於其中的 lambda 抽象. Lambda 表示式能夠捕獲作用域中的變數的無名函數物件, 我們可以將其理解為一個匿名的行內函式, 可以用來替換獨立函數或者函數物件, 從而使程式碼更可讀. 但是從本質上來講, Lambda 表示式只是一種語法糖, 因為它能完成的工作也可以用其他複雜的 C++ 語法來實現.
下面是 Lambda 表示式的語法結構.
[capture] (parameters) mutable -> return-type {statement}
capture
: 捕獲;
parameters
: 引數;
mutable
: 可變規範;
return-type
: 返回型別;
statement
: Lambda 表示式的主體;
下面是更高版本的 C++ 的有關 Lambda 表示式的語法. [1]
[capture] (parameters) lambda說明符 約束(可選) {statement}
[capture] {statement} // (C++23 前)
[capture] lambda說明符 {statement} // (C++23 起)
[capture] < 模板形參 > 約束(可選) // (C++20 起)
(parameters) lambda說明符 約束(可選) {statement} // (C++20 起)
[capture] < 模板形參 > 約束(可選) {statement} // (C++20 起 C++23 前)
[capture] < 模板形參 > 約束(可選) lambda說明符 {statement} //(C++23 起)
捕獲 是一個含有零或更多個捕獲符的逗號分隔列表, 可以 預設捕獲符 開始. 預設捕獲符只有
&
(以參照隱式捕獲被使用的自動變數)和=
(以複製隱式捕獲被使用的自動變數).當出現任一預設捕獲符時, 都能隱式捕獲當前物件(*this). 如果隱式捕獲它, 那麼會始終以參照捕獲,即使預設捕獲符是 =
. 當預設捕獲符為 =
時, *this 的隱式捕獲被棄用. (C++20 起)
當預設捕獲符是 &
時, 後繼的簡單捕獲符不能以 &
開始.
struct S2 { void f(int i); };
void S2::f(int i)
{
[&]{}; // OK:預設以參照捕獲
[&, i]{}; // OK:以參照捕獲,但 i 以值捕獲
[&, &i] {}; // 錯誤:以參照捕獲為預設時的以參照捕獲
[&, this] {}; // OK:等價於 [&]
[&, this, i]{}; // OK:等價於 [&, i]
}
當預設捕獲符是 =
時, 後繼的簡單捕獲符必須以 &
開始, 或者為 *this (C++17 起) 或 this (C++20 起).
struct S2 { void f(int i); };
void S2::f(int i)
{
[=]{}; // OK:預設以複製捕獲
[=, &i]{}; // OK:以複製捕獲,但 i 以參照捕獲
[=, *this]{}; // C++17 前:錯誤:無效語法
// C++17 起:OK:以複製捕獲外圍的 S2
[=, this] {}; // C++20 前:錯誤:= 為預設時的 this
// C++20 起:OK:同 [=]
}
任何捕獲符只可以出現一次, 並且名字不能與形參相同:
struct S2 { void f(int i); };
void S2::f(int i)
{
[i, i] {}; // 錯誤:i 重複
[this, *this] {}; // 錯誤:"this" 重複(C++17)
[i] (int i) {}; // 錯誤:形參和捕獲的名字相同
}
大多數情況下類似於函數的參數列.
C++14 中, 若引數型別是泛型, 則可以使用 auto
宣告型別:
int x[] = {5, 1, 7, 6, 1, 4, 2};
std::sort(x, x + 7, [](int a, int b) { return (a > b); });
for (auto i : x) std::cout << i << " ";
這份程式碼將列印出 x
陣列從大到小排序後的結果.
利用可變規範, Lambda 表示式的主體可以修改通過值捕獲的變數. 若使用此關鍵字, 則 parameters 不可省略 (即使為空).
一個例子, 使用 capture 捕獲字句 中的例子, 來觀察 \(a\) 的值的變化:
int a = 0;
auto func = [a]() mutable { ++a; };
此時 lambda 中的 \(a\) 的值改變為 \(1\), lambda 外的 \(a\) 保持不變.
用於指定 Lambda 表示式的返回型別. 若沒有指定返回型別, 則返回型別將被自動推斷 (行為與用 auto
宣告返回值的普通函數一致). 具體的, 如果函數體中沒有 return
語句, 返回型別將被推導為 void
, 否則根據返回值推導. 若有多個 return
語句且返回值型別不同, 將產生編譯錯誤.
auto lam = [](int a, int b) -> int
Lambda 主體可包含任何函數可包含的部分。普通函數和 Lambda 表示式主體均可存取以下變數型別:
class
中宣告時,若捕獲 this
,則可以存取該物件的成員大部分資料來自 \(\texttt{OI-Wiki}\).