什麼是 lambda 表示式

一個 lambda 表示式提供一個簡明的方式來建立簡單的功能的物件。lambda 表示式是一個 prvalue,其結果物件稱為閉包物件 ,其行為類似於函式物件。

“lambda 表達”這個名字起源於 lambda 演算 ,這是一種數學形式主義,由 Alonzo Church 於 20 世紀 30 年代發明,旨在研究邏輯和可計算性問題。Lambda 演算是 LISP的基礎, LISP是一種函數語言程式設計語言。與 lambda 演算和 LISP 相比,C++ lambda 表示式共享未命名的屬性,並從周圍的上下文中捕獲變數,但它們缺乏操作和返回函式的能力。

lambda 表示式通常用作帶有可呼叫物件的函式的引數。這可能比建立一個命名函式更簡單,該函式僅在作為引數傳遞時使用。在這種情況下,lambda 表示式通常是首選,因為它們允許內聯定義函式物件。

lambda 通常由三部分組成:捕獲列表 [],可選引數列表 () 和 body {},所有這些都可以為空:

[](){}                // An empty lambda, which does and returns nothing

捕獲列表

[]捕獲列表。預設情況下,lambda 無法訪問封閉範圍的變數。捕獲變數使其可以在 lambda 中訪問,可以是副本,可以是引用 。捕獲的變數成為 lambda 的一部分; 與函式引數相比,它們在呼叫 lambda 時不必傳遞。

int a = 0;                       // Define an integer variable
auto f = []()   { return a*9; }; // Error: 'a' cannot be accessed
auto f = [a]()  { return a*9; }; // OK, 'a' is "captured" by value
auto f = [&a]() { return a++; }; // OK, 'a' is "captured" by reference
                                 //      Note: It is the responsibility of the programmer
                                 //      to ensure that a is not destroyed before the
                                 //      lambda is called.
auto b = f();                    // Call the lambda function. a is taken from the capture list and not passed here.

引數列表

()引數列表,與常規函式幾乎相同。如果 lambda 不帶引數,則可以省略這些括號(除非你需要宣告 lambda mutable)。這兩個 lambdas 是等價的:

auto call_foo  = [x](){ x.foo(); };
auto call_foo2 = [x]{ x.foo(); };

Version >= C++ 14

引數列表可以使用佔位符型別 auto 而不是實際型別。通過這樣做,此引數的行為類似於函式模板的模板引數。當你想要在通用程式碼中對向量進行排序時,以下 lambdas 是等效的:

auto sort_cpp11 = [](std::vector<T>::const_reference lhs, std::vector<T>::const_reference rhs) { return lhs < rhs; }; 
auto sort_cpp14 = [](const auto &lhs, const auto &rhs) { return lhs < rhs; }; 

功能體

{}正文,與常規函式相同。

叫一個 lambda

lambda 表示式的結果物件是一個閉包 ,可以使用 operator() 呼叫(與其他函式物件一樣):

int multiplier = 5;
auto timesFive = [multiplier](int a) { return a * multiplier; }; 
std::out << timesFive(2); // Prints 10

multiplier = 15;
std::out << timesFive(2); // Still prints 2*5 == 10

退貨型別

預設情況下,推匯出 lambda 表示式的返回型別。

[](){ return true; };

在這種情況下,返回型別是 bool

你還可以使用以下語法手動指定返回型別:

[]() -> bool { return true; };

可變的 Lambda

預設情況下,lambda 中的值捕獲的物件是不可變的。這是因為生成的閉包物件的 operator() 預設為 const

auto func = [c = 0](){++c; std::cout << c;};  // fails to compile because ++c
                                              // tries to mutate the state of
                                              // the lambda.

使用關鍵字 mutable 可以允許修改,這使得更近的物件的 operator()const

auto func = [c = 0]() mutable {++c; std::cout << c;};

如果與返回型別一起使用,mutable 就會出現在它之前。

auto func = [c = 0]() mutable -> int {++c; std::cout << c; return c;};

一個例子來說明 lambda 的有用性

在 C++ 11 之前:

Version < C++ 11

// Generic functor used for comparison
struct islessthan
{
    islessthan(int threshold) : _threshold(threshold) {}

    bool operator()(int value) const
    {
        return value < _threshold;
    }
private:
    int _threshold;
};

// Declare a vector
const int arr[] = { 1, 2, 3, 4, 5 };
std::vector<int> vec(arr, arr+5);

// Find a number that's less than a given input (assume this would have been function input)
int threshold = 10;
std::vector<int>::iterator it = std::find_if(vec.begin(), vec.end(), islessthan(threshold));

從 C++ 11 開始:

Version >= C++ 11

// Declare a vector
std::vector<int> vec{ 1, 2, 3, 4, 5 };

// Find a number that's less than a given input (assume this would have been function input)
int threshold = 10;
auto it = std::find_if(vec.begin(), vec.end(), [threshold](int value) { return value < threshold; });