自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

這個(gè) C++ 特性如何用十年時(shí)間吃掉所有回調(diào)場(chǎng)景?

開(kāi)發(fā)
終于,C++11 標(biāo)準(zhǔn)橫空出世,帶來(lái)了 lambda 表達(dá)式!這簡(jiǎn)直是革命性的變化!lambda 的設(shè)計(jì)目標(biāo)就是為了能 就地、簡(jiǎn)潔地定義匿名函數(shù)對(duì)象。

嘿,你知道嗎?在 C++11 正式請(qǐng)來(lái) lambda 這位"大咖"之前,C++ 程序員們?yōu)榱藢?shí)現(xiàn)類(lèi)似的功能,可是費(fèi)了不少勁呢!

"史前時(shí)代":函數(shù)對(duì)象(Functors)

想象一下,在沒(méi)有 lambda 的遠(yuǎn)古時(shí)期(C++11 之前),如果你想把一小段"行為"像數(shù)據(jù)一樣傳來(lái)傳去,特別是在用標(biāo)準(zhǔn)庫(kù)算法(比如 sort 或 find_if)的時(shí)候,怎么辦呢?

那時(shí)的主流方法是使用 函數(shù)對(duì)象(Function Objects),也叫 仿函數(shù)(Functors)。這其實(shí)就是重載了圓括號(hào) operator() 的類(lèi)或結(jié)構(gòu)體。它們的對(duì)象可以像函數(shù)一樣被調(diào)用。

// 一個(gè)"老派"的函數(shù)對(duì)象,用于檢查整數(shù)是否大于某個(gè)值 ??
struct IsGreaterThan {
    int threshold; // 仿函數(shù)可以有自己的狀態(tài)(成員變量)
    IsGreaterThan(int t) : threshold(t) {}

    bool operator()(int value) const { // 重載了(),讓對(duì)象可以被調(diào)用
        return value > threshold;
    }
};

// 使用:
#include <vector>      // 需要包含 vector 頭文件
#include <algorithm>   // 需要包含 algorithm 頭文件
#include <iostream>    // 用于輸出

int main() { // 將示例放入 main 函數(shù)中使其可運(yùn)行
    std::vector<int> numbers = {10, 5, 25, 15};
    int limit = 12;
    auto first_big_number_it = std::find_if(numbers.begin(), numbers.end(), IsGreaterThan(limit)); // 創(chuàng)建一個(gè)仿函數(shù)對(duì)象
    if (first_big_number_it != numbers.end()) {
        std::cout << "Found number greater than " << limit << ": " << *first_big_number_it << std::endl; // 輸出 25
    }
    return0;
}

你看,為了實(shí)現(xiàn)一個(gè)簡(jiǎn)單的比較邏輯,就得寫(xiě)一個(gè)完整的 struct。雖然也能用,但總感覺(jué)有點(diǎn)"笨重",代碼不夠簡(jiǎn)潔,尤其是當(dāng)這個(gè)邏輯只需要用一次的時(shí)候。

C++11的"大爆炸":Lambda 登場(chǎng)!

終于,C++11 標(biāo)準(zhǔn)橫空出世,帶來(lái)了 lambda 表達(dá)式!這簡(jiǎn)直是革命性的變化!lambda 的設(shè)計(jì)目標(biāo)就是為了能 就地、簡(jiǎn)潔地定義匿名函數(shù)對(duì)象。

// 使用 C++11 lambda 實(shí)現(xiàn)同樣的功能 ??
#include <vector>
#include <algorithm>
#include <iostream>

int main() { // 同樣放入 main 函數(shù)
    std::vector<int> numbers = {10, 5, 25, 15};
    int limit = 12;
    auto first_big_number_it = std::find_if(numbers.begin(), numbers.end(),
        [limit](int value) { // 看!lambda 多簡(jiǎn)潔!
            return value > limit; // 直接使用捕獲的 limit
        }
    );
    if (first_big_number_it != numbers.end()) {
        std::cout << "Found number greater than " << limit << ": " << *first_big_number_it << std::endl; // 輸出 25
    }
    return0;
}

對(duì)比一下,是不是清爽多了?lambda 不僅語(yǔ)法緊湊,還能方便地"捕獲"外部變量(就像上面例子里的 limit),讓編寫(xiě)簡(jiǎn)短的回調(diào)函數(shù)、自定義排序規(guī)則等變得超級(jí)方便。C++11 的 lambda 奠定了基礎(chǔ),包括捕獲列表 []、參數(shù)列表 () 和函數(shù)體 {} 這些核心要素。

C++14 及后續(xù):不斷進(jìn)化

C++ 標(biāo)準(zhǔn)委員會(huì)顯然也覺(jué)得 lambda 是個(gè)好東西,于是在后續(xù)版本中不斷給它"加技能點(diǎn)":

C++14:

  • 泛型 Lambda (Generic Lambdas):參數(shù)可以用 auto 了,寫(xiě)一次就能處理多種類(lèi)型,就像我們后面會(huì)看到的 auto versatile_add 那樣。
  • 捕獲初始化 (Capture Initializers):允許在捕獲列表 [] 中聲明并初始化新的變量,這些變量只在 lambda 內(nèi)部可見(jiàn)。這對(duì)于移動(dòng)捕獲(move capture)或者創(chuàng)建 lambda 內(nèi)部獨(dú)有的狀態(tài)非常有用。例如 [value = std::move(some_resource)](){ ... }。

C++17:

  • constexpr Lambda:如果 lambda 滿(mǎn)足 constexpr 函數(shù)的要求,那么它可以在編譯時(shí)執(zhí)行!這對(duì)于元編程和提升運(yùn)行時(shí)性能很有幫助。
  • **捕獲 *this**:按值捕獲當(dāng)前對(duì)象的副本,而不是像 [this] 那樣捕獲指針。

C++20:

  • 模板 Lambda (Template Lambdas):可以直接在 lambda 上使用模板參數(shù)了,提供更強(qiáng)的泛型能力。
  • 允許在 無(wú)狀態(tài) lambda(不捕獲任何東西的 lambda)和函數(shù)指針之間進(jìn)行隱式轉(zhuǎn)換。
  • 允許在 lambda 捕獲列表中使用包展開(kāi) (Pack Expansion)。

所以你看,lambda 從最初為了解決函數(shù)對(duì)象寫(xiě)法繁瑣的問(wèn)題,一路進(jìn)化,功能越來(lái)越強(qiáng)大,寫(xiě)法也越來(lái)越靈活,已經(jīng)成為現(xiàn)代 C++ 編程不可或缺的一部分了!

好了,歷史課上完了,咱們接著看怎么用好這位越來(lái)越厲害的 lambda 朋友吧!

使用 lambda 的注意事項(xiàng) (歡樂(lè)版 )

好嘞,各位觀眾!咱們前面認(rèn)識(shí)了 lambda 這位 C++ 世界的新朋友,它像個(gè)可以隨身攜帶的迷你函數(shù)。不過(guò)啊,要想跟這位朋友處好關(guān)系,還得了解它的一些小習(xí)慣和"規(guī)矩"。別擔(dān)心,不復(fù)雜,跟著我來(lái)瞅瞅!

(1) lambda 的"身份證":[](){}

首先,lambda 長(zhǎng)啥樣?它有個(gè)標(biāo)志性的"身份證"——就是這對(duì)兒方括號(hào) []??吹剿珻++ 就知道:"哦豁!一個(gè) lambda 表達(dá)式要來(lái)了!"。

#include <iostream>
#include <vector>
#include <algorithm> // 需要包含 <algorithm> 頭文件

// ... 其他代碼 ...

緊跟在 [] 后面的是我們熟悉的圓括號(hào) (),用來(lái)放參數(shù),就像普通函數(shù)一樣。然后是花括號(hào) {},里面裝著 lambda 要干的活兒。

所以,最簡(jiǎn)單、最"佛系"的 lambda 長(zhǎng)這樣,它啥也不干,就圖個(gè)清靜:

auto zen_lambda = [](){}; // 一個(gè)四大皆空的 lambda... ??

你看這 [](){} 三個(gè)括號(hào)排排坐,是不是有種莫名的萌感?

當(dāng)然啦,實(shí)際中我們不會(huì)寫(xiě)這么"禪意"的 lambda。它通常會(huì)有些代碼。如果 lambda 里面還嵌套了 lambda(套娃警告?。?,記得 保持良好的縮進(jìn),不然自己都可能看暈了。必要時(shí)加點(diǎn)注釋?zhuān)瑯?biāo)明一下 lambda 的結(jié)束位置,就像給它貼個(gè)小標(biāo)簽???。

auto outer_lambda = []() // 外層 lambda 開(kāi)始啦
{
    std::cout << "外面陽(yáng)光明媚~ ??" << std::endl;

    auto inner_lambda = [](int x) // 里面還藏著一個(gè)!
    {
        std::cout << "悄悄告訴你,里面的數(shù)字翻倍是:" << x * 2 << std::endl;
        return x * 2;
    }; // inner_lambda 在這里結(jié)束啦

    inner_lambda(5); // 調(diào)用一下里面的小家伙

}; // outer_lambda 在這里結(jié)束啦,別看丟了哦

outer_lambda(); // 跑起來(lái)看看!

(2) lambda 的"魔法背包":捕獲 []

lambda 最神奇的地方之一,就是它能"捕獲"(Capture)外面的變量,在自己的 {} 地盤(pán)里使用。這個(gè)捕獲的動(dòng)作,就發(fā)生在 [] 這個(gè)"魔法背包"里。

怎么個(gè)帶法呢?主要有兩種打包方式:

  • 按值打包 [=]:這就像是把外面的東西(變量)復(fù)印一份 ??,塞進(jìn)背包。lambda 里面用的是復(fù)印件,安全!就算你對(duì)著復(fù)印件涂涂改改,外面的原件也毫發(fā)無(wú)損。缺點(diǎn)是,你改不了原件。
int pizza_slices = 8; // 外面有8片披薩 ??

    auto eat_pizza_copy = [=]() { // 按值捕獲,拿到的是披薩照片
        // pizza_slices -= 1; // 錯(cuò)誤!??♀? 你不能對(duì)著照片吃披薩
        std::cout << "看著照片,好像有 " << pizza_slices << " 片披薩呢。" << std::endl;
    };

    eat_pizza_copy(); // 輸出:看著照片,好像有 8 片披薩呢。
    std::cout << "外面實(shí)際上還剩 " << pizza_slices << " 片披薩。" << std::endl; // 輸出:外面實(shí)際上還剩 8 片披薩。
看到了吧?lambda 里面的 `pizza_slices` 是個(gè)拷貝,外面該多少還是多少。
  • 按引用打包 [&]:這個(gè)就厲害了,相當(dāng)于給 lambda 一個(gè)直通外面的"對(duì)講機(jī)"。lambda 通過(guò)對(duì)講機(jī)直接跟外面的原件對(duì)話(huà),不僅能看到原件,還能指揮原件改變!效率高,不用復(fù)印。
int cookie_jar = 10; // 曲奇罐里有10塊小餅干 ??

    auto eat_cookie_ref = [&]() { // 按引用捕獲,拿到的是罐子的鑰匙??
        cookie_jar -= 1; // 直接打開(kāi)罐子,吃掉一塊!??
        std::cout << "啊嗚一口,罐子里還剩 " << cookie_jar << " 塊小餅干。" << std::endl;
    };

    eat_cookie_ref(); // 輸出:啊嗚一口,罐子里還剩 9 塊小餅干。
    std::cout << "檢查一下罐子,真的只剩 " << cookie_jar << " 塊了!" << std::endl; // 輸出:檢查一下罐子,真的只剩 9 塊了!
用 `[&]`,lambda 就能修改外面的 `cookie_jar` 了!

但是!注意!前方有坑!

按引用 [&] 捕獲雖然方便,但也藏著風(fēng)險(xiǎn)。就像你把家門(mén)鑰匙給了別人,萬(wàn)一你搬家了(變量銷(xiāo)毀了),那個(gè)人再拿著舊鑰匙回來(lái)開(kāi)門(mén),那可就"查無(wú)此房"了(程序可能就崩了)。

所以,經(jīng)驗(yàn)之談:如果 lambda 只是 "就地"使用(定義了馬上就用,用完就扔),那用 [&] 圖個(gè)方便沒(méi)問(wèn)題。但如果這個(gè) lambda 可能要"活"很久,或者被傳來(lái)傳去,那最好還是用 按值捕獲 [=] 更穩(wěn)妥?;蛘撸_一點(diǎn),在 [] 里 明確寫(xiě)出你要捕獲的變量名,是按值還是按引用,只帶必需品,別一股腦全塞包里!

int apples = 5;      // 5個(gè)蘋(píng)果??
double price = 2.5; // 單價(jià)

// 只按值捕獲蘋(píng)果數(shù)量,按引用捕獲價(jià)格(可能之后要打折?)
auto calculate_cost = [apples, &price](int discount_percent) {
    price = price * (1.0 - discount_percent / 100.0); // 修改引用的價(jià)格
    // apples = 10; // 錯(cuò)誤! 蘋(píng)果是按值捕獲的,不能改
    return apples * price;
};

double total_cost = calculate_cost(10); // 打個(gè)九折
std::cout << "打了折之后," << apples << " 個(gè)蘋(píng)果需要 " << total_cost << " 元。" << std::endl;
std::cout << "現(xiàn)在的蘋(píng)果單價(jià)是 " << price << " 元。" << std::endl;

(3) auto:lambda 的"萬(wàn)能鑰匙" 

你可能注意到,我老是用 auto 來(lái)定義 lambda 變量。為啥?因?yàn)槊總€(gè) lambda 表達(dá)式,哪怕長(zhǎng)得一模一樣,在 C++ 眼里都可能有自己 獨(dú)一無(wú)二、天知地知編譯器知的類(lèi)型。我們?nèi)祟?lèi)是寫(xiě)不出這個(gè)類(lèi)型的名字的(太復(fù)雜了?。?。所以,auto 就成了我們的好幫手,它跟編譯器說(shuō):"嘿,類(lèi)型的事你看著辦,我懶得寫(xiě)了!"。編譯器心領(lǐng)神會(huì),自動(dòng)推導(dǎo)出正確的類(lèi)型。

當(dāng)然,C++ 更鼓勵(lì)我們 "匿名" 使用 lambda,用完即走,不留姓名。這樣代碼更簡(jiǎn)潔,也減少了變量名的煩惱。比如在標(biāo)準(zhǔn)庫(kù)算法里:

std::vector<int> scores = {59, 88, 76, 92, 45};

// 找到第一個(gè)及格的分?jǐn)?shù) (>= 60)
auto first_pass = std::find_if(scores.begin(), scores.end(),
    [](int score) { // 看!沒(méi)有名字的 lambda,直接上!
        return score >= 60;
    } // 這個(gè) lambda 在這里執(zhí)行完任務(wù)就消失了,像個(gè)小精靈??
);

if (first_pass != scores.end()) {
    std::cout << "找到第一個(gè)及格分?jǐn)?shù):" << *first_pass << std::endl; // 輸出 88
}

(4) 泛型 lambda:一招鮮吃遍天

C++14 更進(jìn)一步,讓 lambda 也能玩"泛型"了!咋玩?還是靠 auto 大神!在參數(shù)列表里用 auto,你的 lambda 就能處理多種類(lèi)型的數(shù)據(jù),跟個(gè)變形金剛似的!

// 這個(gè) lambda 可以給任何支持 + 號(hào)的東西做加法
auto versatile_add = [](const auto& a, const auto& b) { // 參數(shù)用了 auto!
    return a + b;
};

std::cout << "整數(shù)加法: " << versatile_add(10, 20) << std::endl;        // 輸出 30
std::cout << "小數(shù)加法: " << versatile_add(3.14, 1.618) << std::endl;    // 輸出 4.758
std::string s1 = "你好,";
std::string s2 = "Lambda!";
std::cout << "字符串拼接: " << versatile_add(s1, s2) << std::endl; // 輸出 你好,Lambda!

是不是超方便?寫(xiě)一次,到處用!

(5) 在類(lèi)里面?別忘了 this 老兄!

如果你的 lambda 定義在類(lèi)的成員函數(shù)里,想訪問(wèn)類(lèi)的成員變量或調(diào)用其他成員函數(shù)怎么辦?很簡(jiǎn)單,把 this 指針也抓進(jìn)背包里!寫(xiě)個(gè) [this] 就行了。這樣 lambda 就知道自己是屬于哪個(gè)對(duì)象的了。

class Counter {
private:
    int count = 0;
public:
    auto get_incrementer() {
        // 捕獲 this 指針,這樣 lambda 內(nèi)部就能訪問(wèn) count 了
        return [this]() {
            this->count++; // 可以訪問(wèn)并修改成員變量 count
            std::cout << "Count is now: " << this->count << std::endl;
        };
    }
};

Counter my_counter;
auto increment = my_counter.get_incrementer();
increment(); // 輸出 Count is now: 1
increment(); // 輸出 Count is now: 2

好啦,關(guān)于 lambda 使用的小貼士就聊到這里!記住它的"身份證" [](){},玩轉(zhuǎn)"魔法背包" [] 的捕獲規(guī)則(= 值,& 引用,或者指明變量),善用 auto 和匿名 lambda,偶爾還可以試試泛型 lambda 和捕獲 this。

希望這些例子和嘮叨能讓你對(duì) lambda 更親切!多用用,你就會(huì)發(fā)現(xiàn)它的妙處了!

責(zé)任編輯:趙寧寧 來(lái)源: everystep
相關(guān)推薦

2015-08-12 13:20:48

2g

2020-12-08 09:18:14

6G通信技術(shù)華為

2019-07-29 07:41:56

程序員技能開(kāi)發(fā)者

2013-05-21 10:19:22

2017-11-07 09:27:16

程序員100萬(wàn)職業(yè)規(guī)劃

2020-05-22 13:27:49

5G網(wǎng)絡(luò)張?jiān)朴?/a>運(yùn)營(yíng)商

2025-04-17 08:09:22

開(kāi)源項(xiàng)目Member

2012-06-28 09:32:15

Windows RTMetro

2013-09-29 09:43:40

戴爾CEO私有化

2024-02-21 11:41:18

2018-07-09 18:12:54

51CTO學(xué)院

2010-02-04 16:07:39

C++回調(diào)函數(shù)

2016-11-21 11:54:34

程序員代碼

2020-12-20 11:21:16

微軟密碼管理安全風(fēng)險(xiǎn)

2016-02-29 11:35:28

阿里云消息隊(duì)列

2017-02-17 11:40:35

服務(wù)器OS

2020-06-11 10:04:50

IPv6網(wǎng)絡(luò)資源

2010-02-01 11:03:36

唐駿跳槽

2014-07-07 11:08:37

監(jiān)聽(tīng)NSA斯諾登

2023-02-24 15:17:34

模型指南
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)