C++ Lambda 表達式詳解:從基礎到實戰

? ? ? ?Lambda 表達式是 C++11 引入的重要特性,它允許我們在代碼中定義匿名函數,極大地簡化了代碼編寫,尤其是在使用 STL 算法和多線程編程時。本文將詳細介紹 Lambda 表達式的語法、特性及實際應用場景。

什么是 Lambda 表達式?

? ? ? ?Lambda 表達式(也稱為 lambda 函數)是一種匿名函數,即沒有函數名的函數。它可以捕獲周圍作用域中的變量,并且可以像對象一樣被傳遞和使用。Lambda 表達式的引入主要是為了簡化函數對象的使用,特別是在需要短小函數作為參數的場景。

Lambda 表達式的基本結構如下:

[capture-list](parameter-list) mutable(optional) exception-specification(optional) -> return-type(optional) {// 函數體
}

各部分含義:

  • capture-list:捕獲列表,指定從周圍作用域捕獲哪些變量及其捕獲方式
  • parameter-list:參數列表,與普通函數的參數列表類似
  • mutable:可選關鍵字,允許在 lambda 體內修改按值捕獲的變量
  • exception-specification:可選,指定 lambda 可能拋出的異常
  • return-type:可選,指定返回類型,若省略,編譯器會自動推導
  • 函數體:lambda 的執行代碼

捕獲列表詳解

捕獲列表決定了 lambda 表達式可以訪問外部作用域中的哪些變量,以及如何訪問(按值或按引用)。

捕獲方式

  1. 按值捕獲[var]?或?[=]

    • [var]:僅按值捕獲變量 var
    • [=]:按值捕獲所有使用到的外部變量
  2. 按引用捕獲[&var]?或?[&]

    • [&var]:僅按引用捕獲變量 var
    • [&]:按引用捕獲所有使用到的外部變量
  3. 混合捕獲

    • [=, &var]:除 var 按引用捕獲外,其余按值捕獲
    • [&, var]:除 var 按值捕獲外,其余按引用捕獲
  4. 空捕獲列表[]

    • 不捕獲任何外部變量

捕獲示例:

#include <iostream>int main() {int a = 10, b = 20;// 空捕獲列表,不能訪問a和bauto lambda1 = []() {std::cout << "Lambda1: 不捕獲任何變量" << std::endl;};// 按值捕獲a,按引用捕獲bauto lambda2 = [a, &b]() {// a = 100; 錯誤:按值捕獲的變量默認是const,不能修改b = 200;   // 正確:可以修改按引用捕獲的變量std::cout << "Lambda2: a=" << a << ", b=" << b << std::endl;};// 按值捕獲所有外部變量auto lambda3 = [=]() {std::cout << "Lambda3: a=" << a << ", b=" << b << std::endl;};// 按引用捕獲所有外部變量auto lambda4 = [&]() {a = 100;b = 300;std::cout << "Lambda4: a=" << a << ", b=" << b << std::endl;};// 混合捕獲:除b按值外,其余按引用auto lambda5 = [&, b]() {a = 200;// b = 400; 錯誤:b是按值捕獲的std::cout << "Lambda5: a=" << a << ", b=" << b << std::endl;};lambda1();lambda2();lambda3();lambda4();lambda5();return 0;
}

mutable 關鍵字

? ? ? ? 默認情況下,按值捕獲的變量在 lambda 體內是只讀的。使用mutable關鍵字可以允許修改按值捕獲的變量:

#include <iostream>int main() {int x = 10;// 不使用mutable,不能修改按值捕獲的xauto lambda1 = [x]() {// x = 20; 錯誤std::cout << "lambda1: x=" << x << std::endl;};// 使用mutable,可以修改按值捕獲的x(但不會影響外部的x)auto lambda2 = [x]() mutable {x = 20;std::cout << "lambda2內部: x=" << x << std::endl;};lambda1();lambda2();std::cout << "外部: x=" << x << std::endl;  // 仍然是10return 0;
}

返回類型

? ? ? ?Lambda 表達式的返回類型通常可以由編譯器自動推導,但在某些情況下(如有多條 return 語句且類型可能不同),需要顯式指定返回類型:?

#include <iostream>int main() {// 自動推導返回類型為intauto add = [](int a, int b) {return a + b;};// 顯式指定返回類型auto divide = [](double a, double b) -> double {if (b == 0) return 0;return a / b;};std::cout << "3 + 5 = " << add(3, 5) << std::endl;std::cout << "10 / 3 = " << divide(10, 3) << std::endl;return 0;
}

Lambda 表達式的實際應用場景:

1. 作為 STL 算法的參數

這是 Lambda 表達式最常見的用途,尤其適合簡短的謂詞函數:

#include <iostream>
#include <vector>
#include <algorithm>int main() {std::vector<int> numbers = {3, 1, 4, 1, 5, 9, 2, 6};// 使用lambda排序(降序)std::sort(numbers.begin(), numbers.end(), [](int a, int b) {return a > b;});// 使用lambda過濾并打印std::cout << "大于3的元素: ";std::for_each(numbers.begin(), numbers.end(), [](int n) {if (n > 3) {std::cout << n << " ";}});std::cout << std::endl;return 0;
}

2. 在多線程中使用

Lambda 表達式非常適合作為線程函數:

#include <iostream>
#include <thread>
#include <vector>int main() {std::vector<std::thread> threads;int shared_data = 0;// 創建5個線程for (int i = 0; i < 5; ++i) {// 捕獲i(按值)和shared_data(按引用)threads.emplace_back([i, &shared_data]() {std::cout << "線程 " << i << " 啟動" << std::endl;// 模擬工作for (int j = 0; j < 1000000; ++j) {shared_data++;}std::cout << "線程 " << i << " 結束" << std::endl;});}// 等待所有線程完成for (auto& t : threads) {t.join();}std::cout << "最終shared_data值: " << shared_data << std::endl;return 0;
}

3. 作為函數返回值

Lambda 表達式可以被包裝在std::function中作為函數返回值:

#include <iostream>
#include <functional>// 返回一個lambda表達式
std::function<int(int)> make_multiplier(int factor) {return [factor](int x) {return x * factor;};
}int main() {auto double_it = make_multiplier(2);auto triple_it = make_multiplier(3);std::cout << "5的兩倍: " << double_it(5) << std::endl;   // 10std::cout << "5的三倍: " << triple_it(5) << std::endl;   // 15return 0;
}

4. 在條件判斷中使用

可以在需要臨時函數的地方直接定義 lambda:

#include <iostream>
#include <vector>
#include <string>int main() {std::vector<std::string> words = {"apple", "banana", "cherry", "date", "elderberry"};int min_length = 5;// 查找第一個長度小于min_length的單詞auto it = std::find_if(words.begin(), words.end(), [min_length](const std::string& word) {return word.length() < min_length;});if (it != words.end()) {std::cout << "第一個短單詞: " << *it << std::endl;  // 輸出 "date"}return 0;
}

C++14 及以后版本的增強

C++14 對 lambda 表達式進行了一些有用的增強:

  1. 泛型 lambda:允許使用auto作為參數類型,使 lambda 更加靈活
// 泛型lambda,可以接受任何類型的參數
auto sum = [](auto a, auto b) {return a + b;
};int main() {std::cout << sum(3, 5) << std::endl;       // 8std::cout << sum(3.5, 2.5) << std::endl;   // 6.0std::cout << sum(std::string("Hello "), std::string("World")) << std::endl;  // Hello Worldreturn 0;
}
  1. 初始化捕獲:可以在捕獲列表中創建和初始化新變量

#include <iostream>
#include <memory>int main() {// 初始化捕獲:創建一個unique_ptr并捕獲它auto lambda = [ptr = std::make_unique<int>(42)]() {std::cout << "值: " << *ptr << std::endl;};lambda();  // 輸出:值: 42return 0;
}

注意事項

  1. 生命周期管理:按引用捕獲局部變量時要特別小心,確保 lambda 在變量銷毀后不再被調用

  2. 性能考量:lambda 表達式通常會被編譯器優化,性能與普通函數相當,但過度使用復雜的 lambda 可能影響可讀性

  3. 調試困難:匿名特性使得調試時可能難以識別特定的 lambda 函數

  4. 捕獲列表的復雜性:過度復雜的捕獲列表可能導致代碼難以理解和維護

總結

? ? ? ? Lambda 表達式是 C++ 中一個非常強大的特性,它提供了一種簡潔、靈活的方式來定義匿名函數。通過合理使用 lambda 表達式,我們可以編寫更簡潔、更易讀的代碼,特別是在使用 STL 算法和多線程編程時。

? ? ? ?掌握 lambda 表達式的關鍵在于理解捕獲列表的工作方式,以及如何在不同場景下選擇合適的捕獲方式。隨著 C++ 標準的不斷發展,lambda 表達式的功能也在不斷增強,成為現代 C++ 編程中不可或缺的工具。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/89946.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/89946.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/89946.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Spring Boot注解詳解

文章目錄前言1. 核心啟動注解SpringBootApplicationEnableAutoConfigurationSpringBootConfiguration2. 組件注解Component及其衍生注解ComponentServiceRepositoryControllerRestController3. 依賴注入注解AutowiredQualifierPrimary4. Web相關注解請求映射注解RequestMapping…

Web開發:ABP框架12——中間件Middleware的創建和使用

一、簡介中間件可以用于鑒權、日志&#xff0c;攔截器可以用于指定方法或url的業務邏輯處理&#xff0c;兩者分工不同&#xff0c;實現效果相似&#xff0c;先執行中間件&#xff0c;后執行攔截器&#xff0c;再到WebAPI接口。二、示例一個Token驗證中間件三、代碼1.Startup.cs…

京東商品評論如何獲取?API接口實戰指南

一、API接入準備1. 注冊開發者賬號訪問京東開放平臺&#xff1a;前往京東開放平臺注冊賬號&#xff0c;完成企業或個人實名認證。創建應用&#xff1a;在控制臺創建應用&#xff0c;獲取App Key和App Secret&#xff08;用于簽名認證&#xff09;。2. 申請API權限搜索接口&…

leetcode-sql-627變更性別

題目&#xff1a; Salary 表&#xff1a; --------------------- | Column Name | Type | --------------------- | id | int | | name | varchar | | sex | ENUM | | salary | int | --------------------- id 是這個表的主鍵…

【學習路線】C#企業級開發之路:從基礎語法到云原生應用

一、C#基礎入門&#xff08;1-2個月&#xff09; &#xff08;一&#xff09;開發環境搭建Visual Studio安裝配置 Visual Studio Community&#xff1a;免費版本&#xff0c;功能完整Visual Studio Code&#xff1a;輕量級&#xff0c;跨平臺支持JetBrains Rider&#xff1a;專…

Planning Agent:基于大模型的動態規劃與ReAct機制,實現復雜問題自適應執行求解

引言 在當今數據驅動的商業環境中&#xff0c;企業面臨著日益復雜的決策問題。傳統的數據分析工具往往難以應對多步驟、多依賴的復雜問題求解。例如&#xff0c;當企業需要分析"北美市場 Q1-Q2 主要產品的銷售增長趨勢并識別關鍵驅動因素"時&#xff0c;傳統工具可能…

人該怎樣活著呢?55

人該怎樣活著呢&#xff1f; A思考現實問題并記錄自己的靈感 。【生活的指南針】 &#xff08;20250212&#xff09; a1如何思考&#xff1f; 當有人問他用什么方法得到那么多發現時&#xff0c;牛頓說&#xff1a;“我只不過對于一件事情&#xff0c;總是花很長時間很熱…

rtthread - V5.1.0版本 HOOK 鉤子函數總結

rtthread - V5.1.0版本 鉤子函數 相對于V4.0.3版本做了很大的修改和優化&#xff1a;舊版本 V4.0.3&#xff1a;rt_thread_inited_sethook(thread_inited_hook);rt_thread_deleted_sethook(thread_deleted_hook);rt_scheduler_sethook(scheduler_hook);新版本 V5.1.0&#xff1…

Python特性:裝飾器解決數據庫長時間斷連問題

前言 在基于 Python 的 Web 應用開發里&#xff0c;數據庫連接是極為關鍵的一環。不過&#xff0c;像網絡波動、數據庫服務器維護這類因素&#xff0c;都可能造成數據庫長時間斷連&#xff0c;進而影響應用的正常運作。本文將詳細介紹怎樣運用 retry_on_failure 裝飾器來解決數…

療愈之手的智慧覺醒:Deepoc具身智能如何重塑按摩機器人的觸覺神經

療愈之手的智慧覺醒&#xff1a;Deepoc具身智能如何重塑按摩機器人的觸覺神經康復中心的理療室內&#xff0c;一位運動員正俯臥在治療床上。機械臂的硅膠觸頭沿腰背肌群緩緩移動&#xff0c;當傳感器捕捉到豎脊肌的異常僵直時&#xff0c;觸頭自動切換高頻震顫模式&#xff1b;…

webpack將組件vue進行編譯混淆,并能正常使用編譯之后的文件

介紹: 我們在開發的過程中有很多組件都需要復用,特別是我們耗費了好幾天時間寫出來的組件,比如自己寫的表格組件,流程圖組件等。總之都是自己不斷測試,不斷編寫耗費了大把的精力寫的。直接用到自己的項目中倒是無所謂,如果是把自己寫的組件給別人,這里就涉及到自己的勞動…

onenote千年老bug,字體bug (calibri微軟雅黑) 的解決

一、如何改這個bug&#xff08;bug是什么在后文&#xff09;一、注意1、有些onenote可能是版本問題&#xff0c;即使提供了設置默認字體的選項&#xff0c;但按教程設置后還是不work&#xff0c;建議升級版本2、親身測過這個方法是可行的&#xff0c;如果不行&#xff0c;考慮下…

麒麟信安參編的三項軟件供應鏈安全團體標準發布

日前&#xff0c;由中國電子商會正式發布了T/CECC 39—2025《信息安全技術 軟件供應鏈管理規范》、T/CECC 40—2025《信息安全技術 軟件供應鏈開源組件檢測要求》以及 T/CECC 41—2025《信息安全技術 軟件供應鏈軟件產品檢測要素和方法》三項重要團體標準。麒麟信安結合自身在軟…

Django ORM系統

1. ORM基礎概念1.1 什么是ORM&#xff1f;ORM&#xff08;Object Relational Mapping&#xff0c;對象關系映射&#xff09;是一種編程技術&#xff0c;用于在面向對象編程語言中實現不同類型系統的數據轉換。在Django中&#xff0c;ORM充當業務邏輯層和數據庫層之間的橋梁。核…

Tailwind CSS中設定寬度和高度的方法

在 Tailwind CSS 中&#xff0c;設定元素的寬度&#xff08;width&#xff09;和高度&#xff08;height&#xff09;有多種方式&#xff0c;涵蓋固定值、相對值、響應式調整等。以下是完整的方法分類及示例&#xff1a;一、固定寬度 / 高度類以 4px (0.25rem) 為單位遞增&…

Java行為型模式---備忘錄模式

備忘錄模式基礎概念備忘錄模式&#xff08;Memento Pattern&#xff09;是一種行為型設計模式&#xff0c;其核心思想是在不破壞封裝性的前提下&#xff0c;捕獲一個對象的內部狀態&#xff0c;并在該對象之外保存這個狀態&#xff0c;以便后續可以將該對象恢復到先前保存的狀態…

后端參數校驗

前端給后端傳輸數據&#xff0c;有時候參數需要校驗&#xff0c;我們自己寫代碼會比較麻煩&#xff0c;我們可以使用springboot為我們提供的注解&#xff0c;降低這些沒有必要的代碼開發。1.引入依賴<dependency><groupId>org.springframework.boot</groupId>…

C++ - 仿 RabbitMQ 實現消息隊列--服務端核心模塊實現(一)

目錄 日志打印工具 實用 Helper 工具 sqlite 基礎操作類 字符串操作類 UUID 生成器類 文件基礎操作 文件是否存在判斷 文件大小獲取 讀文件 寫文件 文件重命名 文件創建/刪除 父級目錄的獲取 目錄創建/刪除 附錄&#xff08;完整代碼&#xff09; 日志打印工具 為了便…

C語言:第07天筆記

C語言&#xff1a;第07天筆記 內容提要 循環結構 break與continue 綜合案例《猜拳游戲》數組 數組的概念一維數組流程控制 break與continue break 功能&#xff1a; ① 用在switch中&#xff0c;用來跳出switch中的case語句&#xff1b;如果case沒有break&#xff0c;可能會產生…

qt 中英文翻譯 如何配置和使用

qt 中英文翻譯 如何配置和使用 1. 在.pro文件中添加TRANSLATIONS 在你的 .pro 文件&#xff08;比如 HYAC_AAF_HOST.pro&#xff09;中添加&#xff1a; TRANSLATIONS \ zh\_CN.ts en\_US.ts這會告訴Qt項目你要支持中文和英文。 2. 提取可翻譯文本&#xff08;生成ts文件&#…