C++11中重要的新特性之 lambda表達式 Part two

序言

?在上一篇文章中,我們主要介紹了 C++11 中的新增的關鍵詞,以及 范圍for循環 這類語法糖的使用和背后的邏輯。在這篇文章中我們會繼續介紹一個特別重要的新特性分別是 lambda表達式


1. lambda表達式

1.1 lambda的定義

?C++11 中的 lambda表達式 是一種定義 匿名函數對象 的方式。它們可以捕獲它們所在作用域中的變量,并且可以在需要函數對象的地方使用,如作為算法的參數。lambda表達式C++11 標準引入后,大大簡化了編碼并增強了代碼的靈活性和可讀性。我們在這里提到 匿名函數對象,說直白點就是該函數沒有具體名字嘛,那是真的沒有嗎?請大家記住這個疑問。

1.2 lambda表達式的基本語法

lambda表達式 的基本語法如下:

[capture](parameters) mutable -> return_type { body }
  • capture:捕獲列表,指定 lambda表達式 體內部可以訪問的外部變量。
  • parameters:參數列表,與普通函數的參數列表類似,但 lambda表達式 也可以沒有參數。
  • mutable:可選的,表示代碼可以修改以值捕獲的外部變量。默認情況下,這些變量是只讀的。
  • return_type:返回類型,可選。編譯器可以根據內容 自動推導返回類型
  • body:表達式的函數體,可以包含任意有效的C++語句。

我們簡單的舉一個示例:

void test_1() {auto func1 = [](int A, int B) ->int { return A + B; };cout << func1(1, 2) << endl;
}

我們返回值在沒有什么特殊需求下完全是可以省略的,編譯器會自動推導,所以我們還可以表示為:

void test_1() {auto func1 = [](int A, int B) { return A + B; };cout << func1(1, 2) << endl;
}

在這里我們的代碼邏輯還是比較簡單的,當我們的代碼邏輯比較復雜時,我們還可以表示為:

void test_1() {auto func1 = [](int A, int B) {  ...... //body};
}

1.3 參數詳解

1. capture 捕獲列表

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

int A = 1, B = 2, C = 1;
auto func2 = [A]() { cout << A << endl; };
func2();

這里就是告訴編譯器你可以使用 A變量,當然了,你想讓他用誰就放誰到捕獲列表中:

int A = 1, B = 2;
auto func2 = [A,B]() { cout << A << endl; cout << B << endl;};
func2();

在這里 [] 的用法可以簡單總結為 3 種:

  • [var]:表示值傳遞方式捕捉變量 var
  • [=]:表示值傳遞方式捕獲所有父作用域中的變量(包括this)
  • [&]:表示引用傳遞捕捉所有父作用域中的變量(包括this)

注意:父作用域指包含 lambda函數 的語句塊

上述的方法甚至還可以混合使用:

int A = 1, B = 2, C = 3;
// 除了 C 都是值傳遞,而 C 是引用傳遞
auto func2 = [=, &C](){// };// 除了 C 都是引用傳遞,而 C 是值傳遞
auto func3 = [&, C](){// };

2. parameters 參數列表

?參數列表的用法和函數的參數列表都是一致的,沒有什么區別。但是如果你沒有需要傳遞的參數時,甚至可以省去:

auto func3 = [A] { cout << A << endl; };

3. mutable 可變的

?當我們的表達式嘗試改變 以值捕獲 的外部變量時,編譯器會報錯,比如:

auto func3 = [A] () { ++A; };

這是因為編譯器規定這些變量只是可讀的,如果你想要進行相應的修改,需要加上 mutable

auto func3 = [A] () mutable { ++A; };

但是這個修改并不會影響 A 本身的的大小,因為是 深拷貝,所以你需要修改 A 本身的大小的話使用引用會更好。

4. return_type 返回類型

?如果你沒什么需要特殊需要的話,完全可以省去,因為編譯器會自動推導,那什么時候是特殊的需求呢,就比如:

double A = 1.5, B = 2.5, C = 3;
auto func2 = [A, B] () -> int{return A * B;};
cout << func2() << endl;

在這里兩個 double 變量相乘推導出肯定是返回 double,但是我想要返回 int 這就可以指定返回類型。

5. body 函數體

?在函數體中,你可以使用參數列表以及捕獲列表中的變量。


1.4 lambda 背后的邏輯

?我們是怎么使用仿函數的呢?就比如一下比較大小的仿函數:

template<class T>
struct Less{bool operator()(const T& left, const T& right){left < right;}
};Less<int> less;
int A = 1, B = 0;
less(A, B);

我們在使用該仿函數之前,先使用創建了一個相應的對象,然后再使用。

再看看我們的 lambda 表達式,我們也是先創建一個變量再通過該變量來調用:auto func = [](){}; 。 但是我們在前面說過, lambda表達式 是一種定義 匿名函數對象 的方式。所有他是真的沒有名字嗎,不是的,他在背后其實編譯器給他取了個名字的,只是我們不需要知道,我們只需要使用 auto 接受接好了,不需要管他叫什么。

那怎么證明呢?我們看看匯編層的邏輯:
在這里插入圖片描述
很明顯是由他是有名字的,只是只有編譯器知道而已。
所以,定義了一個 lambda表達式,編譯器會自動生成一個類,在該類中重載了 operator()


2. lambda 的使用場景

2.1 sort 函數

?如果我們需要對自定義類型進行排序,舉個例子:

class Man {
public:Man(string name, int age) {_name = name;_age = age;}string _name;int _age;
};void test_2() {vector<Man> v;v.emplace_back("L", 10);v.emplace_back("M", 11);v.emplace_back("N", 12);sort(v.begin(), v.end());
}

這樣會報錯,因為自定義類型不可以直接比較,我們在以前的解決方案可以是寫一個仿函數,告訴編譯器怎么比較呀:

struct LessForMansAge {bool operator()(const Man& left, const Man& right) {return left._age < right._age;}
};

現在的話,我們可以直接寫一個仿函數:

auto Less = [](const Man& left, const Man& right) { return left._age < right._age; };
sort(v.begin(), v.end(), Less);

2.2 for_each 函數

?for_each 函數是 C++ 標準庫中的一個算法,它定義在頭文件 中。這個函數用于對給定范圍內的每個元素執行一個指定的操作。for_each 算法提供了一個便利的、統一的方法來遍歷容器(或其他支持迭代器的范圍),并對每個元素執行某個操作,而不需要顯式地編寫循環代碼。
舉個例子,我想要讓數組內的元素 * 2 ,并打印結果:

void test_3() {vector<int> v = { 1, 2, 3, 4, 5, 6 };for_each(v.begin(), v.end(), [](int& x) { x *= 2; });for_each(v.begin(), v.end(), [](int& x) { cout << x << endl; });
}

3. 總結

lambda表達式 的語法更加直觀和靈活,特別是在處理簡單函數或回調函數時。它允許開發者直接在表達式中捕獲外部變量,并定義函數體,這使得代碼更加易于編寫和理解。

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

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

相關文章

昇思25天學習打卡營第19天 | ResNet50遷移學習再續

訓練模型部分代碼解析 構建Resnet50網絡 兩行初始化代碼 weight_init Normal(mean0, sigma0.02)這行代碼定義了一個初始化器weight_init&#xff0c;它將使用均值為0&#xff0c;標準差為0.02的正態分布來初始化網絡中的權重。這種初始化策略有助于在網絡的初始階段避免梯度…

Java基礎之集合

集合和數組的類比 數組: 長度固定可以存基本數據類型和引用數據類型 集合: 長度可變只能存引用數據類型存儲基本數據類型要把他轉化為對應的包裝類 ArrayList集合 ArrayList成員方法 添加元素 刪除元素 索引刪除 查詢 遍歷數組

day30【LeetCode力扣】18.四數之和

day30【LeetCode力扣】18.四數之和 1.題目描述 給你一個由 n 個整數組成的數組 nums &#xff0c;和一個目標值 target 。請你找出并返回滿足下述全部條件且不重復的四元組 [nums[a], nums[b], nums[c], nums[d]] &#xff08;若兩個四元組元素一一對應&#xff0c;則認為兩個…

Linux: Mysql環境安裝

Mysql環境安裝&#xff08;Centos&#xff09; 前言一、卸載多余環境1.1 卸載mariadb1.2 查看并卸載系統mysql和mariadb安裝包 二、換取mysql官方yum源三、安裝并啟動mysql服務3.1 yum源加載3.2 安裝yum源3.3 安裝mysql服務3.3.1 安裝指令3.3.2 GPG密鑰問題解決方法3.3.3 查看是…

循環結構(一)——for語句【互三互三】

文章目錄 &#x1f341; 引言 &#x1f341; 一、語句格式 &#x1f341; 二、語句執行過程 &#x1f341; 三、語句格式舉例 &#x1f341;四、例題 &#x1f449;【例1】 &#x1f680;示例代碼: &#x1f449;【例2】 【方法1】 &#x1f680;示例代碼: 【方法2】…

【C++ 編程】引用 - 給變量起別名、淺復制

基本語法&#xff1a;數據類型 &別名 原名int a 10; int &b a;引用必須初始化 (? int &b;)&#xff0c;初始化后不可改變 (int c 5; b c&#xff1a;b 沒有變成c的別名&#xff0c;而是 a、b 對應的值變更為了 c 的值)本質是指針常量, 淺復制 【黑馬程序員匠…

Cartographer重入門到精通(二):運行作者demo及自己的數據集

在demo數據包上運行cartographer 現在Cartographer和Cartographer的Ros包已經都安裝好了&#xff0c;你可以下載官方的數據集到指定的目錄&#xff08;比如在Deutsches Museum用背包采集的2D和3D 數據&#xff09;&#xff0c;然后使用roslauch來啟動demo。 注&#xff1a;la…

IO半虛擬化-Virtio學習筆記

參考&#xff1a;《深入淺出DPDK》及大佬們的各種博客 Virtio簡介&運行環境 Virtio 是一種用于虛擬化環境中的半虛擬化 I/O 框架&#xff0c;目的是在虛擬機和主機之間提供一種高效的 I/O 機制。關于什么是半虛擬化和全虛擬化&#xff1a;見SR-IOV學習筆記。 YES&#xf…

PDMS二次開發(二十二)——關于1.0.3.1版本升級內容的說明

目錄 1.更新內容介紹2.效果演示3.關于重構自動添加焊口功能的說明3.1錯誤示例 3.問題交流1.創建焊口提示失敗2.程序崩潰 1.更新內容介紹 在添加焊口之前先清除當前branch已有焊口&#xff1b;顯示清除焊口的個數和添加焊口的個數&#xff1b;重構了自動添加焊口功能&#xff0…

值得關注的數據資產入表

不錯的講解視頻&#xff0c;來自&#xff1a;第122期-杜海博士-《數據資源入表及數據資產化》-大數據百家講壇-廈門大學數據庫實驗室主辦第122期-杜海博士-《數據資源入表及數據資產化》-大數據百家講壇-廈門大學數據庫實驗室主辦-20240708_嗶哩嗶哩_bilibili

《A++ 敏捷開發》- 10 二八原則

團隊成員協作&#xff0c;利用項目數據&#xff0c;分析根本原因&#xff0c;制定糾正措施&#xff0c;并立馬嘗試&#xff0c;判斷是否有效&#xff0c;是改善的“基本功”。10-12章會探索里面的注意事項&#xff0c;13章會看兩家公司的實施情況和常見問題。 如果已經獲得高層…

Linq的常用方法

LINQ&#xff08;Language Integrated Query&#xff09;是.NET Framework中用于數據查詢的組件&#xff0c;它將查詢功能集成到C#等.NET語言中。LINQ提供了豐富的查詢操作符&#xff0c;這些操作符可以應用于各種數據源&#xff0c;如內存中的集合、數據庫、XML等。以下是一些…

java中的String 以及其方法(超詳細!!!)

文章目錄 一、String類型是什么String不可變的原因(經典面試題)String不可變的好處 二、String的常用構造形式1.使用常量串構造2.使用newString對象構造3.字符串數組構造 三、常用方法1. length() 獲取字符串的長度2. charAt() 獲取字符串中指定字符的值 (代碼單元)3. codePoin…

水的幾個科學問題及引發的思考

水的幾個科學問題及引發的思考 兩個相同的容器A和B&#xff0c;分別裝有同質量的水&#xff0c;然后&#xff0c;在A容器中加入水&#xff0c;在B容器中加入冰&#xff0c;如果加入水和冰的質量相同。問&#xff0c;容器B的水位將與容器A的水位相同嗎&#xff08;假設冰未融化時…

Log4j的原理及應用詳解(二)

本系列文章簡介&#xff1a; 在軟件開發的廣闊領域中&#xff0c;日志記錄是一項至關重要的活動。它不僅幫助開發者追蹤程序的執行流程&#xff0c;還在問題排查、性能監控以及用戶行為分析等方面發揮著不可替代的作用。隨著軟件系統的日益復雜&#xff0c;對日志管理的需求也日…

MySQL和SQlServer的區別

MySQL和SQlServer的區別 說明&#xff1a;在一些常用的SQL語句中&#xff0c;MySQL和SQLServer存在有一些區別&#xff0c;后續我也會將我遇到的不同點持續更新在這篇博客中。 1. 獲取當前時間 SQLServer&#xff1a; -- SQLServer -- 1.獲取當前時間 SELECT GETDATE(); --…

Vue2切換圖片小案例

代碼中 v-show "index>0",是表示下標只有大于零時上一頁按鈕才會顯示v-show "index<list.length-1",是表示下標只有小于list數組的最大值才會顯示&#xff0c;反之隱藏。click "index--"和click "index",是點擊按鈕后加減數…

【ZooKeeper學習筆記】

1. ZooKeeper基本概念 Zookeeper官網&#xff1a;https://zookeeper.apache.org/index.html Zookeeper是Apache Hadoop項目中的一個子項目&#xff0c;是一個樹形目錄服務Zookeeper翻譯過來就是動物園管理員&#xff0c;用來管理Hadoop&#xff08;大象&#xff09;、Hive&…

AR0132AT 1/3 英寸 CMOS 數字圖像傳感器可提供百萬像素 HDR 圖像處理(器件編號包含:AR0132AT6R、AR0132AT6C)

AR0132AT 1/3 英寸 CMOS 數字圖像傳感器&#xff0c;帶 1280H x 960V 有效像素陣列。它能在線性或高動態模式下捕捉圖像&#xff0c;且帶有卷簾快門讀取。它包含了多種復雜的攝像功能&#xff0c;如自動曝光控制、開窗&#xff0c;以及視頻和單幀模式。它適用于低光度和高動態范…

QML界面控件加載與顯示順序

一、QML界面控件加載順序 QML在界面加載時的順序和我們認知的有很大的不同&#xff0c;有時候會對我們獲取參數以及界面實現造成很大的困擾 1、加載順序 import QtQuick 2.12 import QtQml 2.12 import QtQuick.Window 2.12 import QtQuick.VirtualKeyboard 2.4Window {id: …