[ capture ] ( params ) opt -> ret { body; };
其中 capture 是捕獲列表,params 是參數列,opt 是函數選項,ret 是返回值型別,body是函數體。auto f = [](int a) -> int { return a + 1; }; std::cout << f(1) << std::endl; // 輸出: 2可以看到,上面通過一行程式碼定義了一個小小的功能閉包,用來將輸入加 1 並返回。
auto f = [](int a){ return a + 1; };
這樣編譯器就會根據 return 語句自動推匯出返回值型別。
auto x1 = [](int i){ return i; }; // OK: return type is int
auto x2 = [](){ return { 1, 2 }; }; // error: 無法推匯出返回值型別
auto f1 = [](){ return 1; };
auto f2 = []{ return 1; }; // 省略空參數列
class A { public: int i_ = 0; void func(int x, int y) { auto x1 = []{ return i_; }; // error,沒有捕獲外部變數 auto x2 = [=]{ return i_ + x + y; }; // OK,捕獲所有外部變數 auto x3 = [&]{ return i_ + x + y; }; // OK,捕獲所有外部變數 auto x4 = [this]{ return i_; }; // OK,捕獲this指標 auto x5 = [this]{ return i_ + x + y; }; // error,沒有捕獲x、y auto x6 = [this, x, y]{ return i_ + x + y; }; // OK,捕獲this指標、x、y auto x7 = [this]{ return i_++; }; // OK,捕獲this指標,並修改成員的值 } }; int a = 0, b = 1; auto f1 = []{ return a; }; // error,沒有捕獲外部變數 auto f2 = [&]{ return a++; }; // OK,捕獲所有外部變數,並對a執行自加運算 auto f3 = [=]{ return a; }; // OK,捕獲所有外部變數,並返回a auto f4 = [=]{ return a++; }; // error,a是以複製方式捕獲的,無法修改 auto f5 = [a]{ return a + b; }; // error,沒有捕獲變數b auto f6 = [a, &b]{ return a + (b++); }; // OK,捕獲a和b的參照,並對b做自加運算 auto f7 = [=, &b]{ return a + (b++); }; // OK,捕獲所有外部變數和b的參照,並對b做自加運算從上例中可以看到,lambda 表示式的捕獲列表精細地控制了 lambda 表示式能夠存取的外部變數,以及如何存取這些變數。
int a = 0; auto f = [=]{ return a; }; // 按值捕獲外部變數 a += 1; // a被修改了 std::cout << f() << std::endl; // 輸出?在這個例子中,lambda 表示式按值捕獲了所有外部變數。在捕獲的一瞬間,a 的值就已經被複製到f中了。之後 a 被修改,但此時 f 中儲存的 a 仍然還是捕獲時的值,因此,最終輸出結果是 0。
int a = 0; auto f1 = [=]{ return a++; }; // error,修改按值捕獲的外部變數 auto f2 = [=]() mutable { return a++; }; // OK,mutable需要注意的一點是,被 mutable 修飾的 lambda 表示式就算沒有引數也要寫明參數列。
std::function<int(int)> f1 = [](int a){ return a; }; std::function<int(void)> f2 = std::bind([](int a){ return a; }, 123);另外,對於沒有捕獲任何變數的 lambda 表示式,還可以被轉換成一個普通的函數指標:
using func_t = int(*)(int); func_t f = [](int a){ return a; }; f(123);lambda 表示式可以說是就地定義彷函數閉包的“語法糖”。它的捕獲列表捕獲住的任何外部變數,最終均會變為閉包型別的成員變數。而一個使用了成員變數的類的 operator(),如果能直接被轉換為普通的函數指標,那麼 lambda 表示式本身的 this 指標就丟失掉了。而沒有捕獲任何外部變數的 lambda 表示式則不存在這個問題。
typedef void(*Ptr)(int*); Ptr p = [](int* p){delete p;}; // 正確,沒有狀態的lambda(沒有捕獲)的lambda表示式可以直接轉換為函數指標 Ptr p1 = [&](int* p){delete p;}; // 錯誤,有狀態的lambda不能直接轉換為函數指標上面第二行程式碼能編譯通過,而第三行程式碼不能編譯通過,因為第三行的程式碼捕獲了變數,不能直接轉換為函數指標。
class CountEven { int& count_; public: CountEven(int& count) : count_(count) {} void operator()(int val) { if (!(val & 1)) // val % 2 == 0 { ++ count_; } } }; std::vector<int> v = { 1, 2, 3, 4, 5, 6 }; int even_count = 0; for_each(v.begin(), v.end(), CountEven(even_count)); std::cout << "The number of even is " << even_count << std::endl;這樣寫既煩瑣又容易出錯。有了 lambda 表示式以後,我們可以使用真正的閉包概念來替換掉這裡的仿函數,程式碼如下:
std::vector<int> v = { 1, 2, 3, 4, 5, 6 }; int even_count = 0; for_each( v.begin(), v.end(), [&even_count](int val) { if (!(val & 1)) // val % 2 == 0 { ++ even_count; } }); std::cout << "The number of even is " << even_count << std::endl;lambda 表示式的價值在於,就地封裝短小的功能閉包,可以極其方便地表達出我們希望執行的具體操作,並讓上下文結合得更加緊密。