C++中的Lambda表達(dá)式探究

C++11 及之后的版本中,C++ 提供了 lambda 表達(dá)式,它是一種方便了參數(shù)傳遞和定義匿名函數(shù)的方法。該方法通常用于封裝算法、執(zhí)行異步方法 ,也就是說比較適用于少量的代碼。以下原文:

In C++11 and later, a lambda expression—often called a lambda—is a convenient way of defining an anonymous function object (a closure) right at the location where it is invoked or passed as an argument to a function. Typically lambdas are used to encapsulate a few lines of code that are passed to algorithms or asynchronous methods. This article defines what lambdas are, compares them to other programming techniques, describes their advantages, and provides a basic example.

1. Lambda 表達(dá)式的組成

先對(duì) lambda 表達(dá)式有一個(gè)直觀的認(rèn)識(shí),參考下面程序,該程序完成的是將輸入的數(shù)組 nums 按照絕對(duì)值大小進(jìn)行升序排列。

int main() {
    std::vector<int> nums = {1, 5, 3, 4, 2, -1, 10};
    std::sort(nums.begin(), nums.end(), [](int a, int b) mutable throw() -> bool {
        // lambda 表達(dá)式函數(shù)體,在這里做到了將輸入數(shù)組升序排列
        return (std::abs(a) < std::abs(b));
    });
    for (int i : nums) std::cout << i << " ";
    // >: 1 -1 2 3 4 5 10
}

拋開邊邊角角,單獨(dú)拿出最重要的一部分來學(xué)習(xí),[](int a, int b) mutable throw() -> bool{ // statement } 就是 lambda 表達(dá)式最原始的內(nèi)容。在該表達(dá)式中,每一部分的含義如下敘述:

  1. [] 捕獲字句:用來捕獲周圍范圍中出現(xiàn)的變量,也被稱為引導(dǎo)子句,可以在其中聲明獲取的變量是還是引用,默認(rèn)值為 & ,上文中的例子和 [&] 是一樣的效果,具體例子見下文。
  2. () 參數(shù)列表:用來獲取參數(shù),對(duì)于一個(gè)一般的 lambda 函數(shù),使用起來和一般的指針函數(shù)沒有區(qū)別,也是需要有參數(shù)列表的,具體例子見下文。
  3. mutable 可變類型(可選):一般來說,在 lambda 體中調(diào)用運(yùn)算符的變量,都是以 const value 來使用的,加上這個(gè) mutable 之后,人家變成了變量來使用,具體栗子見下文。
  4. throw() 異常類型(可選):和普通函數(shù)一樣樣,lambda 函數(shù)也可能引發(fā)異常,如果不會(huì)引發(fā)異常的話,直接聲明 noexcept 就可以啦~
  5. -> bool 返回類型(可選):繼續(xù)和普通函數(shù)一樣
  6. {// statement } lambda 體:和一般的函數(shù)體一樣。

不難發(fā)現(xiàn),lambda 函數(shù)和一般的函數(shù)沒有太大區(qū)別,基本上只有在頭部位置有特殊語(yǔ)法。

2. 捕獲語(yǔ)句的使用 & 可變規(guī)范 mutable

拿出栗子:

int main() {
    int num = 1; // 在上文中聲明好變量 num
    auto f = [n = num]() { // 在下文中通過 捕獲[] 來獲取 num,并在 lambda 函數(shù)體中進(jìn)行使用
        std::cout << n << std::endl;
        // std::cout << ++num << std::endl; // 錯(cuò)誤的使用,因?yàn)?num 是不可變的常量
    };
    f(); // >: 1
    auto m = [num]() mutable {
        std::cout << ++num << std::endl; // 將內(nèi)部變量聲明成 mutable 可變類型,此時(shí)可以修改內(nèi)部變量
    };
    m(); // >: 2
    std::cout << num << std::endl; // >: 2
}

mutable 可變聲明優(yōu)勢(shì)在于可以在內(nèi)部直接改變外部變量,相當(dāng)于使用了 [&num] 引用方法。

3. 參數(shù)列表

再拿出一個(gè)栗子:

int main() {
    auto y = [](int a, int b) {
        return a + b;
    };
    std::cout << y(3, 2); // >: 5
}

從這里開始,也就是參數(shù)列表開始,后面的內(nèi)容都是可選項(xiàng),也就是如果為空,那么就直接省略不寫即可。例如:

int main() {
    auto empty = [] {
        std::cout << "Wow!空的~" << std::endl;
        // 啥也沒有只有個(gè)函數(shù)體};
    };
    empty(); // >: Wow!空的~
}

4. 特殊用法

4.1 花里胡哨的 lambda 嵌套

int main() {
    // 兩層 lambda 嵌套,看起來挺花里胡哨
    auto embed_embed_lambda = [](int a) {
        std::cout << a << " - - ";
        return [](int c) { return c / 2; };
    };
    std::cout << embed_embed_lambda(2)(2) << std::endl; // >: 2 - - 1
}

4.2 高階 lambda 函數(shù)

高階函數(shù)是指,采用另一個(gè) lambda 表達(dá)式作為其參數(shù)或返回 lambda 表達(dá)式的 lambda 表達(dá)式(不知不覺想起了俄羅斯套娃??)

int main() {
    // 返回 function 對(duì)象的 lambda 表達(dá)式
    auto get_function = [](int x) -> std::function<int(int)> {
        return [=](int y) { return x + y; };
    };
    // 使用 function 為對(duì)象作為其參數(shù)的 lambda 表達(dá)式
    auto param_function = [](const std::function<int(int)> &f, int n) {
        return f(n) * 2;
    };
    auto ans = param_function(get_function(2), 3); // x = 2, n = 3
    std::cout << ans << std::endl;
}

5. 總結(jié)

寫到此處,關(guān)于 C++lambda 語(yǔ)法規(guī)范和用法已經(jīng)學(xué)習(xí)了一小部分,它作為一種方便靈活的方法隨用隨學(xué)也是闊以的。

因?yàn)閰?shù)類型和函數(shù)模板參數(shù)一樣可以被推導(dǎo)而無需和具體參數(shù)類型耦合,有利于重構(gòu)代碼;和使用auto聲明變量的作用類似,它也允許避免書寫過于復(fù)雜的參數(shù)類型。特別地,不需要顯式指出參數(shù)類型使得使用高階函數(shù)變得更加容易。

以下程序源碼:

/**
 * Created by Xiaozhong on 2020/8/30.
 * Copyright (c) 2020/8/30 Xiaozhong. All rights reserved.
 */
#include <functional>
#include <algorithm>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> nums = {1, 5, 3, 4, 2, -1, 10};
    std::sort(nums.begin(), nums.end(), [&](int a, int b) mutable throw() -> bool {
        // lambda 表達(dá)式函數(shù)體,在這里做到了將輸入數(shù)組升序排列
        return (std::abs(a) < std::abs(b));
    });
    for (int i : nums) std::cout << i << " ";
    // >: 1 -1 2 3 4 5 10

    int num = 1; // 在上文中聲明好變量 num
    auto f = [n = num]() { // 在下文中通過 捕獲[] 來獲取 num,并在 lambda 函數(shù)體中進(jìn)行使用
        std::cout << n << std::endl;
        // std::cout << ++num << std::endl; // 錯(cuò)誤的使用,因?yàn)?num 是不可變的常量
    };
    f(); // >: 1
    auto m = [num]() mutable {
        std::cout << ++num << std::endl; // 將內(nèi)部變量聲明成 mutable 可變類型,此時(shí)可以修改內(nèi)部變量
    };
    m(); // >: 2
    std::cout << num << std::endl; // >: 2

    auto y = [](int a, int b) {
        return a + b;
    };
    std::cout << y(3, 2); // >: 5

    auto empty = [] {
        std::cout << "Wow!空的~" << std::endl;
        // 啥也沒有只有個(gè)函數(shù)體};
    };
    empty(); // >: Wow!空的~

    // 聲明一個(gè)函數(shù),然后直接使用 (5, 3)
    int n = [](int a, int b) { return a + b; }(5, 3);
    std::cout << n << std::endl; // >: 8

    // 兩層 lambda 嵌套,看起來挺花里胡哨
    auto embed_embed_lambda = [](int a) {
        std::cout << a << " - - ";
        return [](int c) { return c / 2; };
    };
    std::cout << embed_embed_lambda(2)(2) << std::endl; // >: 2 - - 1

    // 返回 function 對(duì)象的 lambda 表達(dá)式
    auto get_function = [](int x) -> std::function<int(int)> {
        return [=](int y) { return x + y; };
    };

    // 使用 function 為對(duì)象作為其參數(shù)的 lambda 表達(dá)式
    auto param_function = [](const std::function<int(int)> &f, int n) {
        return f(n) * 2;
    };

    auto ans = param_function(get_function(2), 3);
    std::cout << ans << std::endl;
}

Algorithms
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容