【C++入門到精通】 Lambda表達式 C++11 [ C++入門 ]

在這里插入圖片描述

閱讀導航

  • 引言
  • 一、C++98中的一個例子
  • 二、Lambda表達式
    • 1. Lambda表達式語法
      • (1)Lambda表達式各部分說明
      • (2)捕獲列表說明
  • 三、Lambda表達式的底層原理
  • 溫馨提示

引言

當今軟件開發行業的快速發展和日益復雜的需求,要求程序員們具備靈活而高效的編程技巧。在這樣的背景下,C++11引入了一項強大而令人興奮的特性:lambda表達式。lambda表達式為C++程序員提供了一種簡潔、靈活且強大的方式來定義和使用匿名函數。通過lambda表達式,我們可以將函數作為一等公民對待,更加方便地實現函數對象的傳遞和使用。它不僅提供了一種新的編碼方式,還使得代碼更易于理解和維護。

在本文中,我們將深入探討C++11中lambda表達式的語法、特性和用法。我們將介紹如何定義lambda表達式,如何捕獲外部變量,并演示lambda表達式在各種場景下的實際應用。無論您是C++開發新手還是有經驗的老手,本文都將為您提供全面而深入的指導,幫助您充分發揮lambda表達式的威力,提升代碼的可讀性和性能。請各位坐穩扶好,咱們要開車了😍!!!

一、C++98中的一個例子

在C++98中,如果想要對一個數據集合中的元素進行排序,可以使用std::sort方法。如果待排序元素為自定義類型,需要用戶定義排序時的比較規則:

struct Goods
{string _name; // 名字double _price; // 價格int _evaluate; // 評價Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){}
};
struct ComparePriceLess
{bool operator()(const Goods& gl, const Goods& gr){return gl._price < gr._price;}
};
struct ComparePriceGreater
{bool operator()(const Goods& gl, const Goods& gr){return gl._price > gr._price;}
};
int main()
{vector<Goods> v = { { "蘋果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2, 3 }, { "菠蘿", 1.5, 4 } };sort(v.begin(), v.end(), ComparePriceLess());sort(v.begin(), v.end(), ComparePriceGreater());
}

隨著C++語法的發展,人們開始覺得上面的寫法太復雜了,每次為了實現一algorithm算法,都要重新去寫一個類,感覺就為了一盤醋包了餃子。如果每次比較的邏輯不一樣,還要去實現多個類,特別是相同類的命名,這些都給編程者帶來了極大的不便。因此,在C++11語法中出現了Lambda表達式。

二、Lambda表達式

int main()
{vector<Goods> v = { { "蘋果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2, 3 }, { "菠蘿", 1.5, 4 } };sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){return g1._price < g2._price; });sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){return g1._price > g2._price; });sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){return g1._evaluate < g2._evaluate; });sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){return g1._evaluate > g2._evaluate; });
}

上述代碼就是使用C++11中的lambda表達式來解決,可以看出lambda表達式實際是一個匿名函數。是不是感覺目的明朗了好多?這就是lambda表達式的魅力。

1. Lambda表達式語法

lambda表達式的書寫格式為:

[capture-list] (parameters) mutable -> return-type { statement 
}

(1)Lambda表達式各部分說明

?其中各部分的含義如下:

  • capture-list捕獲列表,該列表總是出現在lambda函數的開始位置,編譯器根據[ ]來判斷接下來的代碼是否為lambda函數,捕捉列表能夠捕捉上下文中的變量供lambda函數使用
  • parameters:參數列表,類似于普通函數的參數列表用于接收傳入的參數。如果不需要參數傳遞,則可以連同( )一起省略
  • mutable:可選關鍵字,用于指示是否可以修改被捕獲的值。默認情況下,lambda函數總是一個const函數,mutable可以取消其常量性。使用該修飾符時,參數列表不可省略(即使參數為空)
  • return-type:返回值類型。用追蹤返回類型形式聲明函數的返回值類型,沒有返回值時此部分可省略。返回值類型明確情況下,也可省略,由編譯器對返回類型進行推導。
  • {statement}:函數體。在該函數體內,除了可以使用其參數外,還可以使用所有捕獲到的變量。

🚨注意
在lambda函數定義中,參數列表和返回值類型都是可選部分,而捕捉列表和函數體可以為空因此C++11中最簡單的lambda函數為:[ ] { }; 該lambda函數不能做任何事情

int main()
{// 最簡單的lambda表達式, 該lambda表達式沒有任何意義[]{};// 省略參數列表和返回值類型,返回值類型由編譯器推導為intint a = 3, b = 4;[=]{return a + 3; };// 省略了返回值類型,無返回值類型auto fun1 = [&](int c){b = a + c; };fun1(10)cout<<a<<" "<<b<<endl;// 各部分都很完善的lambda函數auto fun2 = [=, &b](int c)->int{return b += a+ c; };cout<<fun2(10)<<endl;// 復制捕捉xint x = 10;auto add_x = [x](int a) mutable { x *= 2; return a + x; };cout << add_x(10) << endl;return 0;
}

通過上述例子可以看出,lambda表達式實際上可以理解為無名函數,該函數無法直接調用,如果想要直接調用,可借助auto將其賦值給一個變量

(2)捕獲列表說明

捕捉列表描述了上下文中那些數據可以被lambda使用,以及使用的方式傳值還是傳引用。

  • [var]:表示值傳遞方式捕捉變量var,即在lambda表達式內部以值的方式使用外部變量var。
  • [=]:表示值傳遞方式捕獲所有父作用域中的變量,包括this指針。在lambda表達式內部以值的方式使用所有外部變量。
  • [&var]:表示引用傳遞捕捉變量var,即在lambda表達式內部以引用的方式使用外部變量var。
  • [&]:表示引用傳遞捕捉所有父作用域中的變量,包括this指針。在lambda表達式內部以引用的方式使用所有外部變量。
  • [this]:表示值傳遞方式捕捉當前的this指針,可以在lambda表達式內部使用當前對象的成員變量和成員函數。

通過這些捕獲方式,可以靈活地控制lambda表達式對外部變量的訪問方式,從而實現不同的功能需求。

🚨注意

  1. 父作用域指的是包含lambda函數的語句塊或函數體
  2. 捕獲列表可以由多個捕獲項組成,并以逗號分隔。每個捕獲項可以使用不同的捕獲方式,如值傳遞或引用傳遞。
    • 例如 [=, &a, &b] 表示以引用傳遞的方式捕捉變量a和b,以值傳遞的方式捕捉其他所有變量。
    • 例如 [&, a, this] 表示以值傳遞的方式捕捉變量a和this,以引用傳遞的方式捕捉其他變量。
  3. 捕獲列表不允許重復捕捉同一個變量,否則會導致編譯錯誤
    • 例如 [=, a] 中的= a 重復捕捉了變量a。
  4. 在不是塊作用域的地方定義的lambda函數,捕獲列表必須為空。
  5. 在塊作用域中定義的lambda函數只能捕捉父作用域中的局部變量,捕捉其他作用域或非局部變量將導致編譯錯誤。
  6. lambda表達式之間不能相互賦值,即使看起來類型相同
void (*PF)();
int main()
{auto f1 = []{cout << "hello world" << endl; };auto f2 = []{cout << "hello world" << endl; };//f1 = f2; // 編譯失敗--->提示找不到operator=()// 允許使用一個lambda表達式拷貝構造一個新的副本auto f3(f2);f3();// 可以將lambda表達式賦值給相同類型的函數指針PF = f2;PF();return 0;
}

三、Lambda表達式的底層原理

?函數對象

函數對象,又稱為仿函數,即可以想函數一樣使用的對象,就是在類中重載了operator()運算符的類對象

class Rate
{
public:Rate(double rate): _rate(rate){}double operator()(double money, int year){ return money * _rate * year;}
private:double _rate;
};int main()
{// 函數對象double rate = 0.49;Rate r1(rate);r1(10000, 2);// lambdaauto r2 = [=](double monty, int year)->double{return monty*rate*year;};r2(10000, 2);return 0;
}

從使用方式上來看,函數對象與lambda表達式完全一樣實際在底層編譯器對于lambda表達式的處理方式,完全就是按照函數對象的方式處理的,即:如果定義了一個lambda表達式,編譯器會自動生成一個類,在該類中重載了operator( )
在這里插入圖片描述

當我們使用Lambda表達式時,實際上是在定義一個匿名函數對象。這個匿名函數對象可以捕獲其所在作用域中的變量,并且可以像函數一樣被調用。Lambda表達式的底層原理涉及到以下幾個步驟:

  1. 閉包類型的生成:編譯器會根據Lambda表達式的定義,隱式地生成一個與Lambda表達式對應的閉包類型。這個閉包類型實際上是一個匿名的類類型,它包含Lambda表達式的執行體,以及捕獲的外部變量。

  2. 捕獲列表的處理:Lambda表達式可以通過捕獲列表指定在其作用域外部定義的變量的訪問方式。捕獲列表告訴編譯器應該以值傳遞還是引用傳遞的方式來捕獲變量,并且根據捕獲列表生成對應的閉包類型的成員變量。

  3. 閉包對象的生成:當Lambda表達式被創建時,實際上是生成了一個對應的閉包對象,同時也會生成對應的閉包類型。這個閉包對象包含了Lambda表達式的執行體,以及捕獲的外部變量的值或引用。

  4. 函數調用操作符的重載:生成的閉包類型重載了函數調用操作符operator(),并且在其中實現了Lambda表達式的執行體。當我們調用Lambda表達式時,實際上是在調用這個閉包對象的operator(),從而執行Lambda表達式的代碼。

通過以上步驟,Lambda表達式在底層實際上是通過生成閉包類型、處理捕獲列表、生成閉包對象,并重載函數調用操作符來實現的。這些機制使得Lambda表達式能夠在C++中方便地定義匿名函數,并捕獲其所在作用域中的變量。

溫馨提示

感謝您對博主文章的關注與支持!另外,我計劃在未來的更新中持續探討與本文相關的內容,會為您帶來更多關于C++以及編程技術問題的深入解析、應用案例和趣味玩法等。請繼續關注博主的更新,不要錯過任何精彩內容!

再次感謝您的支持和關注。期待與您建立更緊密的互動,共同探索C++、算法和編程的奧秘。祝您生活愉快,排便順暢!
在這里插入圖片描述

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

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

相關文章

基于openwrt創建應用程序教程

背景 之前在做路由器工作時&#xff0c;搞過一段時間openwrt&#xff0c;最近看到之前寫的筆記。整理一下&#xff0c;希望能幫助一些朋友入坑。 熟悉openwrt openwrt之前并沒有接觸過&#xff0c;其目錄結構和linux也有所不同。先大致了解一下openwrt文件系統中各個目錄的作用…

C運算符與表達式

跟著肯哥&#xff08;不是我&#xff09;學運算符與表達式 運算符 在C語言中&#xff0c;運算符是一種用來執行特定操作的符號或關鍵字。它們用于對變量、常量和表達進行計算、邏輯判斷和位操作等。 定義一般都當耳旁風了 運算符分類 算術運算符 -*/%加減乘除取模&#xff0c;…

浮點數運算精度丟失,如何解決

為什么浮點數運算的時候會有精度丟失的風險&#xff1f; 浮點數運算精度丟失代碼演示&#xff1a; float a 2.0f - 1.9f; float b 1.8f - 1.7f; System.out.println(a);// 0.100000024 System.out.println(b);// 0.099999905 System.out.println(a b);// false為什么會出現…

everything排除目錄

everything默認搜索所有文件&#xff0c;自己把沒啥必要的目錄都屏蔽掉&#xff0c;記錄如下

ChatGPT/GPT4丨編程助手;AI畫圖;數據分析;科研/項目實現;提示詞工程技巧;論文寫作等

ChatGPT 在論文寫作與編程方面也具備強大的能力。無論是進行代碼生成、錯誤調試還是解決編程難題&#xff0c;ChatGPT都能為您提供實用且高質量的建議和指導&#xff0c;提高編程效率和準確性。此外&#xff0c;ChatGPT是一位出色的合作伙伴&#xff0c;可以為您提供論文寫作的…

【微軟技術棧】使用新的C#功能減少內存分配

本文內容 通過引用傳遞和返回引用安全上下文安全的上下文和 ref 結構統一內存類型通過參考安全提高性能 本節中介紹的技術可提高應用于代碼中的熱路徑時的性能。熱路徑是代碼庫中在正常操作中經常重復執行的部分。將這些技術應用于不經常執行的代碼將產生最小的影響。在進行任何…

[數據結構]-AVL樹

前言 作者&#xff1a;小蝸牛向前沖 名言&#xff1a;我可以接受失敗&#xff0c;但我不能接受放棄 如果覺的博主的文章還不錯的話&#xff0c;還請點贊&#xff0c;收藏&#xff0c;關注&#x1f440;支持博主。如果發現有問題的地方歡迎?大家在評論區指正 目錄 一、AVL樹基…

OpenGL 繪制線(Qt)

文章目錄 一、簡介二、實現代碼三、實現效果參考資料一、簡介 這里同樣對OpenGL中的繪制線操作進行封裝,便于后續的操作,很多形狀也都是基于線來生成的,如圓形等。 二、實現代碼 LineDrawable.h #ifndef LINE_DRAWABLE_H #define LINE_DRAWABLE_H#include

IBM ELM—系統工程全生命周期管理平臺

產品概述 Engineering Lifecycle Management是IBM提供的工程全生命周期管理組合工具&#xff0c;幫助企業降低開發成本&#xff0c;應對開發挑戰并更快地發展其流程和實踐。 隨著產品變得更加復雜且數字化&#xff0c;傳統的工程開發不再能及時且有效地滿足系統工程的復雜度&a…

【Django-DRF】多年md筆記第5篇:Django-DRF的Request、Response和視圖詳解

本文從分析現在流行的前后端分離Web應用模式說起&#xff0c;然后介紹如何設計REST API&#xff0c;通過使用Django來實現一個REST API為例&#xff0c;明確后端開發REST API要做的最核心工作&#xff0c;然后介紹Django REST framework能幫助我們簡化開發REST API的工作。 Dj…

[點云分割] 基于最小切割的分割

效果&#xff1a; 代碼&#xff1a; #include <iostream> #include <vector>#include <pcl/point_types.h> #include <pcl/io/pcd_io.h> #include <pcl/visualization/cloud_viewer.h> #include <pcl/filters/filter_indices.h> #include…

Can‘t open the append-only file: Permission denied

redis rdb aof-CSDN博客 Cant open the append-only file: Permission denied E:\Document_Redis_Windows\redis-2.4.5-win32-win64\64bit E:\Document_Redis_Windows\redis-2.4.5-win32-win64\64bit\redis.conf 還是不行&#xff0c;就要修改權限了&#xff0c;windows【完全控…

matlab 最小二乘擬合平面并與XOY平面對齊

目錄 一、算法原理二、代碼實現1、繞原點對齊2、繞質心對齊三、結果展示1、繞原點對齊2、繞質心對齊四、測試數據本文由CSDN點云俠原創,原文鏈接。爬蟲網站自重。如果你不是在點云俠的博客中看到該文章,那么此處便是不要臉的爬蟲。 一、算法原理 首先,使用最小二乘擬合平面…

priority_queue簡單實現(優先級隊列)(c++)

priority_queue priority_queue介紹邏輯實現框架調整算法adjust_up()adjust_down() 仿函數/比較函數仿函數特性 構造函數迭代器區間構造 完整優先級隊列代碼 priority_queue介紹 pri_que是一個容器適配器&#xff0c;它的底層是其他容器&#xff0c;并由這些容器再封裝而來。類…

C語言指針相關練習題

? C語言指針相關練習題 文章目錄 C語言指針相關練習題題目一題目二題目三題目四題目五題目六題目七 題目一 #include <stdio.h> int main() {int a[5] { 1, 2, 3, 4, 5 };int *ptr (int *)(&a 1);printf( "%d,%d", *(a 1), *(ptr - 1));return 0; }…

[Unity+OpenAI TTS] 集成openAI官方提供的語音合成服務,構建海王暖男數字人

1.簡述 最近openAI官方發布了很多新功能&#xff0c;其中就包括了最新發布的TTS語音合成服務的api接口。說到這個語音合成接口&#xff0c;大家可能會比較陌生&#xff0c;但是說到chatgpt官方應用上的聊天機器人&#xff0c;那個臺灣腔的海王暖男的聲音&#xff0c;可能就有印…

深度合成算法的基礎與原理

深度合成算法是人工智能領域中備受矚目的研究方向之一。它的應用范圍涵蓋了圖像合成、文本生成、音頻合成等多個領域&#xff0c;為人們提供了令人驚嘆的創新和娛樂體驗。本文將深入探討深度合成算法的基礎原理&#xff0c;了解它們是如何工作的以及它們在不同領域的應用。算法…

輕量封裝WebGPU渲染系統示例<38>- 動態構建WGSL材質Shader(源碼)

實現原理: 基于宏定義和WGSL功能文件實現 當前示例源碼github地址: https://github.com/vilyLei/voxwebgpu/blob/feature/rendering/src/voxgpu/sample/DynamicShaderBuilding.ts 當前示例運行效果: 此示例基于此渲染系統實現&#xff0c;當前示例TypeScript源碼如下&#x…

編寫bat程序 快速開啟 redis 服務

一鍵開啟redis服務 編寫txt文件&#xff0c;代碼如下&#xff1a;cd /d E:\Redis\Redis-x64-5.0.14.1 redis-server.exe redis.windows.conf這里的redis的安裝目錄記得改成自己的 將文件后綴的.txt改成.bat&#xff0c;然后雙擊運行就可以啦

前綴和及差分數組

前綴和 原數組x0x1x2x3x4x5前綴和數組x0x0x1x0x1x2x0x1x2x3x0x1x2x3x4x0x1x2x3x4x5前綴和數組代數形式x0’x1’x2’x3’x4’x5’ 計算原數組某區間的和 sum[x1,x2,x3] 利用前綴和計算 x3-x0 x0x1x2x3-x0 x1x2x3 差分數組 x0x1x2x3x4x5原數組x0x1x2x3x4x5差分數組x0x1-x0x…