C++的Lambda表示式
今天寫LeetCode題的時候看到了別的大佬在排序的時候用到了Lambda表示式,但自己對此還是一知半解,所以在這裡介紹下。
1. 概述
C++ 11 中的 Lambda 表示式用於定義並建立匿名的函式物件,以簡化程式設計工作。
Lambda 的語法形式如下:
[函式物件引數] (操作符過載函式引數) mutable 或 exception 宣告 -> 返回值型別 {函式體}
可以看到,Lambda 主要分為五個部分:[函式物件引數]、(操作符過載函式引數)、mutable 或 exception 宣告、-> 返回值型別、{函式體}.
2. Lambda 語法分析
2.1 [函式物件引數]
標識一個 Lambda 表示式的開始,這部分必須存在,不能省略。函式物件引數是傳遞給編譯器自動生成的函式物件類的建構函式的。函式物件引數只能使用那些到定義 Lambda 為止時 Lambda 所在作用範圍內可見的區域性變數(包括 Lambda 所在類的 this)。函式物件引數有以下形式:
- 空。沒有任何函式物件引數。
- =。函式體內可以使用 Lambda 所在範圍內所有可見的區域性變數(包括 Lambda 所在類的 this),並且是值傳遞方式(相當於編譯器自動為我們按值傳遞了所有區域性變數)。
- &。函式體內可以使用 Lambda 所在範圍內所有可見的區域性變數(包括 Lambda 所在類的 this),並且是引用傳遞方式(相當於是編譯器自動為我們按引用傳遞了所有區域性變數)。
- this。函式體內可以使用 Lambda 所在類中的成員變數。
- a。將 a 按值進行傳遞。按值進行傳遞時,函式體內不能修改傳遞進來的 a 的拷貝,因為預設情況下函式是 const 的,要修改傳遞進來的拷貝,可以新增 mutable 修飾符。
- &a。將 a 按引用進行傳遞。
- a,&b。將 a 按值傳遞,b 按引用進行傳遞。
- =,&a,&b。除 a 和 b 按引用進行傳遞外,其他引數都按值進行傳遞。
- &,a,b。除 a 和 b 按值進行傳遞外,其他引數都按引用進行傳遞。
2.2 (操作符過載函式引數)
標識過載的 () 操作符的引數,沒有引數時,這部分可以省略。引數可以通過按值(如: (a, b))和按引用 (如: (&a, &b)) 兩種方式進行傳遞。
2.3 mutable 或 exception 宣告
這部分可以省略。按值傳遞函式物件引數時,加上 mutable 修飾符後,可以修改傳遞進來的拷貝(注意是能修改拷貝,而不是值本身)。exception 宣告用於指定函式丟擲的異常,如丟擲整數型別的異常,可以使用 throw(int)。
2.4 -> 返回值型別
標識函式返回值的型別,當返回值為 void,或者函式體中只有一處 return 的地方(此時編譯器可以自動推斷出返回值型別)時,這部分可以省略。
2.5 {函式體}
標識函式的實現,這部分不能省略,但函式體可以為空。
3. 示例
[] (int x, int y) { return x + y; } // 隱式返回型別
[] (int& x) { ++x; } // 沒有 return 語句 -> Lambda 函式的返回型別是 'void'
[] () { ++global_x; } // 沒有引數,僅訪問某個全域性變數
[] { ++global_x; } // 與上一個相同,省略了 (操作符過載函式引數)
可以像下面這樣顯示指定返回型別:
[] (int x, int y) -> int { int z = x + y; return z; }
在這個例子中建立了一個臨時變數 z 來儲存中間值。和普通函式一樣,這個中間值不會儲存到下次呼叫。什麼也不返回的Lambda 函式可以省略返回型別,而不需要使用 -> void 形式。
Lambda 函式可以引用在它之外宣告的變數. 這些變數的集合叫做一個閉包. 閉包被定義在 Lambda 表示式宣告中的方括號 [] 內。這個機制允許這些變數被按值或按引用捕獲。如下圖的例子:
3.1 示例 1
std::vector<int> some_list;
int total = 0;
for (int i = 0; i < 5; ++i) some_list.push_back(i);
std::for_each(begin(some_list), end(some_list), [&total](int x)
{
total += x;
});
此例計算 list 中所有元素的總和。變數 total 被存為 Lambda 函式閉包的一部分。因為它是棧變數(區域性變數)total 引用,所以可以改變它的值。
3.2 示例 2
std::vector<int> some_list;
int total = 0;
int value = 5;
std::for_each(begin(some_list), end(some_list), [&, value, this](int x)
{
total += x * value * this->some_func();
});
此例中 total 會存為引用, value 則會存一份值拷貝。對 this 的捕獲比較特殊,它只能按值捕獲。this 只有當包含它的最靠近它的函式不是靜態成員函式時才能被捕獲。對 protect 和 private 成員來說,這個 Lambda 函式與建立它的成員函式有相同的訪問控制。如果 this 被捕獲了,不管是顯式還是隱式的,那麼它的類的作用域對 Lambda 函式就是可見的。訪問 this 的成員不必使用 this-> 語法,可以直接訪問。