lambda 表達式在 C++11 之後的版本中是一種定義匿名函數的簡潔語法。在一些長環境下,例如 STL 算法和異步操作,使用 lambda 作為回調函數非常自然和方便,並且 lambda 和 C++ 的函數式編程特性非常契合。
基本語法#
一個完整的 lambda 表達式語法如下:
[caputreList](paramList) mutable throw() -> returnType {
// do something..
}
捕獲列表#
lambda 以一個中括號的捕獲列表開始,捕獲列表用來定義哪些外部變量可以被 lambda 表達式使用,以及如何使用(傳值,引用)。
捕獲列表可以為空,表示 lambda 不使用閉包之外的變量,但中括號不可省略。
無論是傳值還是引用方式,都可以使用默認的捕獲方式,在默認捕獲方式下,編譯器會自動分析 lambda 需要捕獲哪些外部變量。默認傳值捕獲方式和默認的引用捕獲方式可以混用,具體用法如下:
[=] // 默認傳值捕獲
[&] // 默認引用捕獲
[&, factor] // 除factor傳值捕獲外,其他變量使用引用捕獲。
[=. factor] // 除factor引用捕獲外,其他變量使用傳值捕獲。
捕獲列表不允許重複聲明,這種重複包括捕獲方式重複和變量重複:
[&, &factor] // 錯誤, & 已經聲明了默認的捕獲方式. &factor 重複
[=, factor] // 同上
[factor, factor] // 錯誤,變量重複
自動捕獲提供了更方便的捕獲機制,但是可能會導致無意間捕獲不需要的變量,可能會到之前在的內存使用增加或引入性能問題。
傳值還是引用捕獲的選擇一般需要通過特定的場景來選擇,一般來說,傳值還是引用的捕獲方式選擇和正常函數傳參方式的規則差不多。
捕獲列表同樣可以使用可變參數模板:
[arg...]
在類成員函數體中使用 lambda 表達式時,需要把this
指針顯式捕獲,以提供對類成員函數和數據的訪問權限。
[this, filter]
C++14 中引入了一種新的默認捕獲方式,可以在捕獲字句中引入並初始化新的變量,而無需把這些變量放在閉包內,這對於一些需要考慮作用域和聲明周期的變量非常有用:
auto p = make_uniqure<T>(factor);
auto _lambda = [ptr = move(p)]{}
參數列表#
lambda 的參數列表行為等大多數方面類似於標準函數的參數列表。
捕獲列表和參數列表在某種意義上都可以用來給閉包提供變量,但捕獲列表適用於哪些在定義 lambda 時就已知且在後續調用中不會改變的變量。
捕獲列表適用於在整個 lambda 的聲明週期中保持不變的變量,參數列表適用於在每次調用 lambda 時都不同的變量。
std::vector<int> num = {1,2,3,4};
int fixedValue = 10;
std::for_each(num.begin(). num.end(), [fixedValue](int &x){x += fixedValue;});
在參數列表中,如果參數類型是泛型的,則使用 auto 告知編譯器創建模板:
[](auto factor)
mutable#
一般地,lambda 的函數調用時 const-by-value 的,但是加入 mutable 規範後,閉包可以修改值捕獲的變量:
異常#
throw()
用來指示 lambda 拋出異常,可以省略,也可以使用 noexcept 指示不拋出異常:
[]() noexcpet {}
[]() throw() {}
[]() {}
返回類型#
在 lambda 中 返回類型指示可以省略,由編譯器自動推斷,但也有例外:
// C++11以及以前,某些編譯器
[](int i){
if (i < 0){
return i;
}
else{
return -i;
}
} // 錯誤,推斷類型為void
這是由於閉包的operator()
推斷規則:
如果主體只包含 return 一個表達式,返回類型為表達式類型,否則返回類型為 void。 (C++ 14 之前)。
lambda 體#
基本行為和一般函數一致。