C++——lambda表达式
介紹
lambda表達式是一種局部類類型,它含有一個構造函數,和一個const成員函數operator()()。
lambda表達式除了能做參數外,還能用于初始化一個聲明為auto或者std::function<R(LA)>的變量。R是lambda的返回類型,LA是它的類型參數列表。
組成部件
實現模型
對于lambda表達式而言,有著很多種表述方法,并且有著多中優化途徑。如果把lambda表達式看成是一種定義并使用函數對象的便捷方式,那么便于理解。
假設有這么一個例子:
void print_modulo(const vector<int>& v, ostream& os, int m) {for_each(begin(v), end(v),[&os, m](int x) {if (x % m == 0) os << x << '/n';}); }我們可以將其中的lambda表達式改寫成一個函數對象:
class Modulo_print {ostream& os;int m; public :Modulo_print(ostream& s, int mm) :os{ s }, m{ mm } {}void operator() (int x) const {if (x % m == 0) os << x << '/n';} };并且改寫之前的例子:
void print_modulo(const vector<int>& v, ostream& os, int m) {for_each(begin(v), end(v),Modulo_print{os, m}); }我們把由lambda表達式生成的函數對象稱為閉包對象 (閉包)。
如果lambda表達式通過引用 ([&]) 捕獲它定義環境中的每一個局部變量,則其閉包對象則可以優化為簡單的包含一個指向外層棧框架的指針。
捕獲
lambda表達式的主要用途是封裝一部分代碼以便將其用做參數。
有些lambda表達式無需訪問它的局部環境,這樣lambda表達式使用空引入符 [ ] 定義。
lambda引入符的形式有很多:
對于lambda表達式來說,當把lambda傳遞給另一個線程時,用值捕獲更優,通過引用或者指針訪問其他線程中的內容是一種危險操作,對于性能和正確性來說都是如此。
對于可變模板參數的捕獲示例:
template <typename...Var> void algo(int s, Var... var) { auto helper = [&s, &var...]{return s * (h1(var...) + h2(var...)); }; }生命周期
lambda表達式的生命周期可能比它的被調用者更長。當我們把lambda表達式傳遞給另一個線程或者存在別處以供后續使用的時候。
例如:
當setup完成之后,我們可能需要畫一個三角型,此時lambda表達式將會訪問一個已經不存在的局部變量。
如果我們發現一個lambda表達式的生命周期可能比它的調用者更長,則我們需要確保所有局部信息都已經被拷貝到閉包對象中去。
m.add("draw a triangle", [=]{m.draw(p1, p2, p3); });lambda與this
當lambda表達式被用在成員函數當中,我們怎樣去訪問類成員變量呢?我們通過捕獲this指針來訪問類成員對象。
例如:
類成員變量是通過this訪問的,在lambda表達式中[this] 和 [=] 互不兼容,因此一不小心就可能在多線程程序中造成競爭條件。
mutable的lambda表達式
如果希望在lambda表達式修改函數對象的狀態,即修改通過值捕獲的變量,則可以使用mutable修飾符:
void algo() {int count = 100;[count]()mutable {return --count;}; }lambda表達式與返回類型
lambda表達式的大多數規則都是從類和函數借鑒過來的,然而需要有兩點需要注意:
如果在一條lambda表達式的主體部分不包含return語句,則其返回類型為void。
如果lambda表達式只包含一條return語句,則返回類型是return表達式的類型。 其他情況,必須顯式定義一個返回類型。
lambda表達式的類型
任意兩個lambda表達式的類型都不相同,一旦兩個lambda表達式的類型相同,則模板實例化機制就無法識別它們了。
例如:
auto algo = [&algo](char* b, char* a) {swap(*b, *--a); algo(++b, a);};由于algo的類型并沒有在使用之前被推斷出來,因此上面的寫法是錯誤的。
使用function包裝器可以存入函數對象。就可以保證,在使用之前, algo的類型就已經知道了。
function<void (char*b, char*a)> algo = [&](char* b, char* a) {swap(*b, *--a); algo(++b, a);};如果我們只是想給lambda表達式起個名字,而不會在主體內部遞歸的使用它。則:
auto algo = [&](char* b, char* a) {swap(*b, *--a); };是沒有問題的。
如果lambda表達式什么也不捕獲,則我們可以把它賦值給一個指向正確類型的函數指針。
double (*fun) (double a) = [](double a) {return sqrt(a);};但行好事, 莫問前程!
總結
以上是生活随笔為你收集整理的C++——lambda表达式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 粗糙表面的微表面模型——Physical
- 下一篇: 对于C++的思考