C++ 中的 Lambda 運算式
在 Visual C++ 中,被稱為「Lambda」的 Lambda 運算式像是一個維護狀態的匿名函式,也可以存取封閉範圍可用的變數。 它會定義類別和建構物件該類型的物件。 本文定義什麼是 Lambda、Lambda 與其他程式設計技術的比較、描述 Lamdba 的優點並提供基本範例。
關於 Lambda
許多程式語言支援「匿名函式」(Anonymous Function) 的概念,即具有主體但沒有名稱的函式。 Lambda 是與匿名函式相關的程式設計技術。 Lambda 可隱含定義函式物件類別和建構該類別類型之函式物件。 如需函式物件的詳細資訊,請參閱函式物件。
Lambda 的入門範例 ISO C++ 標準示範如何在參數內容中傳遞 Lambda 至 std::sort() 函式:
#include <algorithm>
#include <cmath>
void abssort(float* x, unsigned n) {
std::sort(x, x + n,
// Lambda expression begins
[](float a, float b) {
return (std::abs(a) < std::abs(b));
} // end of lambda expression
);
}
本文說明這個運算式的運作方式。
重要
下列通用語言執行平台 (CLR) Managed 實體不支援 Lambda:ref class、ref struct、value class 或 value struct。
函式物件與Lambdas
當您撰寫程式碼時,您可能會使用函式指標和函式物件解決問題和執行計算,尤其是在使用 STL 演算法時。 函式指標和函式物件各有優缺點。例如,函式指標的語法額外負荷最小,但是無法在範圍內保留狀態,而函式物件則可以維護狀態但無法避免需要定義類別的語法額外負荷。
Lambda 結合了函式指標和函式物件的優點並避免其缺點。 Lambda 像函式物件一樣具有彈性並可以維護狀態,但不同於函式物件,由於語法精簡,因此不需要定義類別。 使用 Lambda 時,您可以撰寫相對函式物件而言較不麻煩且較不容易發生錯誤的程式碼。
下列範例比較使用函式物件與使用 Lambda。 第一個範例使用 Lambda 將 vector 物件中每個偶數和奇數項目列印到主控台。 第二個範例使用函式物件完成相同的工作。
範例 1:使用 Lambda
這個範例使用內嵌在 for_each 函式中的 Lambda 呼叫,以將 vector 物件中的每個偶數和奇數項目列印到主控台。
程式碼
// even_lambda.cpp
// compile with: cl /EHsc /nologo /W4 /MTd
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
// Create a vector object that contains 10 elements.
vector<int> v;
for (int i = 1; i < 10; ++i) {
v.push_back(i);
}
// Count the number of even numbers in the vector by
// using the for_each function and a lambda.
int evenCount = 0;
for_each(v.begin(), v.end(), [&evenCount] (int n) {
cout << n;
if (n % 2 == 0) {
cout << " is even " << endl;
++evenCount;
} else {
cout << " is odd " << endl;
}
});
// Print the count of even numbers to the console.
cout << "There are " << evenCount
<< " even numbers in the vector." << endl;
}
輸出
註解
在此範例中,for_each 函式的第三個引數是 Lambda。 [&evenCount] 組件指定運算式的擷取子句、(int n) 指定參數清單,而其餘組件則指定運算式的主體。
範例 2:使用函式物件
有時候,Lambda 的靈巧度較差,因此擴充程度無法超越上一個範例。 下一個範例不使用 Lambda,而是使用函式物件搭配 for_each 函式產生和範例 1 相同的結果。 這兩個範例都會將偶數計數儲存在 vector 物件中。 為了維護作業的狀態,FunctorClass 類別會以傳址方式儲存 m_evenCount 變數做為成員變數。 FunctorClass 會實作函式呼叫運算子 operator() 以執行此作業。 Visual C++ 編譯器會產生與範例 1. 中 Lambda 程式碼大小和效能相當的程式碼。 就本文所述此類基礎問題而言,較簡單的 Lambda 設計應該比函式物件的設計好。 不過,如果您認為未來可能會需要大幅擴充,那麼使用函式物件設計會讓程式碼較容易維護。
如需 operator() 的詳細資訊,請參閱函式呼叫 (C++)。 如需 for_each 函式的詳細資訊,請參閱 for_each。
程式碼
// even_functor.cpp
// compile with: /EHsc
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
class FunctorClass
{
public:
// The required constructor for this example.
explicit FunctorClass(int& evenCount)
: m_evenCount(evenCount) { }
// The function-call operator prints whether the number is
// even or odd. If the number is even, this method updates
// the counter.
void operator()(int n) const {
cout << n;
if (n % 2 == 0) {
cout << " is even " << endl;
++m_evenCount;
} else {
cout << " is odd " << endl;
}
}
private:
// Default assignment operator to silence warning C4512.
FunctorClass& operator=(const FunctorClass&);
int& m_evenCount; // the number of even variables in the vector.
};
int main()
{
// Create a vector object that contains 10 elements.
vector<int> v;
for (int i = 1; i < 10; ++i) {
v.push_back(i);
}
// Count the number of even numbers in the vector by
// using the for_each function and a function object.
int evenCount = 0;
for_each(v.begin(), v.end(), FunctorClass(evenCount));
// Print the count of even numbers to the console.
cout << "There are " << evenCount
<< " even numbers in the vector." << endl;
}
輸出
摘要
Lambda 是強大且明確的程式設計技術。 若要了解 Lambda 的組件和屬性,請參閱 Lambda 運算式語法。 若要了解在程式中如何使用 Lambda,請參閱Lambda 運算式的範例。