成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

這個 C++ 特性如何用十年時間吃掉所有回調場景?

開發
終于,C++11 標準橫空出世,帶來了 lambda 表達式!這簡直是革命性的變化!lambda 的設計目標就是為了能 就地、簡潔地定義匿名函數對象。

嘿,你知道嗎?在 C++11 正式請來 lambda 這位"大咖"之前,C++ 程序員們為了實現類似的功能,可是費了不少勁呢!

"史前時代":函數對象(Functors)

想象一下,在沒有 lambda 的遠古時期(C++11 之前),如果你想把一小段"行為"像數據一樣傳來傳去,特別是在用標準庫算法(比如 sort 或 find_if)的時候,怎么辦呢?

那時的主流方法是使用 函數對象(Function Objects),也叫 仿函數(Functors)。這其實就是重載了圓括號 operator() 的類或結構體。它們的對象可以像函數一樣被調用。

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

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

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

int main() { // 將示例放入 main 函數中使其可運行
    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)); // 創建一個仿函數對象
    if (first_big_number_it != numbers.end()) {
        std::cout << "Found number greater than " << limit << ": " << *first_big_number_it << std::endl; // 輸出 25
    }
    return0;
}

你看,為了實現一個簡單的比較邏輯,就得寫一個完整的 struct。雖然也能用,但總感覺有點"笨重",代碼不夠簡潔,尤其是當這個邏輯只需要用一次的時候。

C++11的"大爆炸":Lambda 登場!

終于,C++11 標準橫空出世,帶來了 lambda 表達式!這簡直是革命性的變化!lambda 的設計目標就是為了能 就地、簡潔地定義匿名函數對象。

// 使用 C++11 lambda 實現同樣的功能 ??
#include <vector>
#include <algorithm>
#include <iostream>

int main() { // 同樣放入 main 函數
    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 多簡潔!
            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;
}

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

C++14 及后續:不斷進化

C++ 標準委員會顯然也覺得 lambda 是個好東西,于是在后續版本中不斷給它"加技能點":

C++14:

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

C++17:

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

C++20:

  • 模板 Lambda (Template Lambdas):可以直接在 lambda 上使用模板參數了,提供更強的泛型能力。
  • 允許在 無狀態 lambda(不捕獲任何東西的 lambda)和函數指針之間進行隱式轉換。
  • 允許在 lambda 捕獲列表中使用包展開 (Pack Expansion)。

所以你看,lambda 從最初為了解決函數對象寫法繁瑣的問題,一路進化,功能越來越強大,寫法也越來越靈活,已經成為現代 C++ 編程不可或缺的一部分了!

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

使用 lambda 的注意事項 (歡樂版 )

好嘞,各位觀眾!咱們前面認識了 lambda 這位 C++ 世界的新朋友,它像個可以隨身攜帶的迷你函數。不過啊,要想跟這位朋友處好關系,還得了解它的一些小習慣和"規矩"。別擔心,不復雜,跟著我來瞅瞅!

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

首先,lambda 長啥樣?它有個標志性的"身份證"——就是這對兒方括號 []。看到它,C++ 就知道:"哦豁!一個 lambda 表達式要來了!"。

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

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

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

所以,最簡單、最"佛系"的 lambda 長這樣,它啥也不干,就圖個清靜:

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

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

當然啦,實際中我們不會寫這么"禪意"的 lambda。它通常會有些代碼。如果 lambda 里面還嵌套了 lambda(套娃警告!),記得 保持良好的縮進,不然自己都可能看暈了。必要時加點注釋,標明一下 lambda 的結束位置,就像給它貼個小標簽???。

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

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

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

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

outer_lambda(); // 跑起來看看!

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

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

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

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

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

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

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

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

但是!注意!前方有坑!

按引用 [&] 捕獲雖然方便,但也藏著風險。就像你把家門鑰匙給了別人,萬一你搬家了(變量銷毀了),那個人再拿著舊鑰匙回來開門,那可就"查無此房"了(程序可能就崩了)。

所以,經驗之談:如果 lambda 只是 "就地"使用(定義了馬上就用,用完就扔),那用 [&] 圖個方便沒問題。但如果這個 lambda 可能要"活"很久,或者被傳來傳去,那最好還是用 按值捕獲 [=] 更穩妥。或者,更精確一點,在 [] 里 明確寫出你要捕獲的變量名,是按值還是按引用,只帶必需品,別一股腦全塞包里!

int apples = 5;      // 5個蘋果??
double price = 2.5; // 單價

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

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

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

你可能注意到,我老是用 auto 來定義 lambda 變量。為啥?因為每個 lambda 表達式,哪怕長得一模一樣,在 C++ 眼里都可能有自己 獨一無二、天知地知編譯器知的類型。我們人類是寫不出這個類型的名字的(太復雜了!)。所以,auto 就成了我們的好幫手,它跟編譯器說:"嘿,類型的事你看著辦,我懶得寫了!"。編譯器心領神會,自動推導出正確的類型。

當然,C++ 更鼓勵我們 "匿名" 使用 lambda,用完即走,不留姓名。這樣代碼更簡潔,也減少了變量名的煩惱。比如在標準庫算法里:

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

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

if (first_pass != scores.end()) {
    std::cout << "找到第一個及格分數:" << *first_pass << std::endl; // 輸出 88
}

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

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

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

std::cout << "整數加法: " << versatile_add(10, 20) << std::endl;        // 輸出 30
std::cout << "小數加法: " << 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!

是不是超方便?寫一次,到處用!

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

如果你的 lambda 定義在類的成員函數里,想訪問類的成員變量或調用其他成員函數怎么辦?很簡單,把 this 指針也抓進背包里!寫個 [this] 就行了。這樣 lambda 就知道自己是屬于哪個對象的了。

class Counter {
private:
    int count = 0;
public:
    auto get_incrementer() {
        // 捕獲 this 指針,這樣 lambda 內部就能訪問 count 了
        return [this]() {
            this->count++; // 可以訪問并修改成員變量 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

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

希望這些例子和嘮叨能讓你對 lambda 更親切!多用用,你就會發現它的妙處了!

責任編輯:趙寧寧 來源: everystep
相關推薦

2015-08-12 13:20:48

2g

2020-12-08 09:18:14

6G通信技術華為

2019-07-29 07:41:56

程序員技能開發者

2013-05-21 10:19:22

2017-11-07 09:27:16

程序員100萬職業規劃

2020-05-22 13:27:49

5G網絡張云勇運營商

2025-04-17 08:09:22

開源項目Member

2012-06-28 09:32:15

Windows RTMetro

2016-11-21 11:54:34

程序員代碼

2013-09-29 09:43:40

戴爾CEO私有化

2024-02-21 11:41:18

2020-06-11 10:04:50

IPv6網絡資源

2010-02-01 11:03:36

唐駿跳槽

2018-07-09 18:12:54

51CTO學院

2016-02-29 11:35:28

阿里云消息隊列

2020-12-20 11:21:16

微軟密碼管理安全風險

2017-02-17 11:40:35

服務器OS

2010-02-04 16:07:39

C++回調函數

2022-03-28 11:41:21

物聯網物聯網市場智能電網

2010-07-28 16:07:51

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: av网站免费看| 国产精品久久久久久久白浊 | 国产精品色 | 成人av免费| 日韩免费一区 | 欧美乱码精品一区二区三区 | 国产精品久久久久久久久久久新郎 | 日产精品久久久一区二区福利 | av网站免费观看 | 青青草av网站 | 99福利视频导航 | 免费午夜视频 | 亚洲综合一区二区三区 | 天堂久久网 | 国产成人免费视频网站高清观看视频 | 黄色在线免费观看 | 国产乱人伦 | 九色视频网站 | 国产欧美一区二区三区国产幕精品 | 亚洲视频一区在线播放 | 91精品久久久 | 欧美在线看片 | 免费成人高清在线视频 | 久久精品小视频 | 黄色一级大片在线免费看产 | 亚洲男人天堂网 | 成年人黄色一级毛片 | 国产视频中文字幕在线观看 | 国产一区二区黑人欧美xxxx | 成人一区二区三区在线观看 | 伊人网综合在线观看 | 欧美日韩国产一区二区 | 日韩精品一区二区三区视频播放 | 成人国产免费视频 | 97精品超碰一区二区三区 | 不卡一二区 | 一区二区成人 | 请别相信他免费喜剧电影在线观看 | 秋霞影院一区二区 | 国产欧美在线一区二区 | 一区二区电影 |