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.mzph.cn/web/44717.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java基础之集合

集合和数组的类比 数组: 长度固定可以存基本数据类型和引用数据类型 集合: 长度可变只能存引用数据类型存储基本数据类型要把他转化为对应的包装类 ArrayList集合 ArrayList成员方法 添加元素 删除元素 索引删除 查询 遍历数组

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】…

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章会看两家公司的实施情况和常见问题。 如果已经获得高层…

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;假设冰未融化时…

【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: …

九盾安防:如何调控叉车限速器的报警速度呢

在繁忙的物流仓储和制造业环境中&#xff0c;叉车是不可或缺的搬运设备。然而&#xff0c;其高速行驶也带来了潜在的安全隐患。为了确保作业人员和货物的安全&#xff0c;又车限速器的设置显得尤为关键。那么&#xff0c;如何调控叉车限速器的报警速度呢? 叉车限速器的速度调整…

复制vmware虚拟机文件并改名(文件名使用python替换)得到一台新的虚拟机

文章目录 需求实验复制文件夹并重命名使用python将所有文件名“WinSer2022”字符替换成“wingetmac”修改虚拟机配置文件&#xff08;.vmx&#xff09;打开新的虚拟机成功 需求 将已有的Winser2022虚拟机复制成wingetmac并开机 实验 复制文件夹并重命名 将"WinSer2022…

了解并缓解 IP 欺骗攻击

欺骗是黑客用来未经授权访问计算机或网络的一种网络攻击&#xff0c;IP 欺骗是其他欺骗方法中最常见的欺骗类型。通过 IP 欺骗&#xff0c;攻击者可以隐藏 IP 数据包的真实来源&#xff0c;使攻击来源难以知晓。一旦访问网络或设备/主机&#xff0c;网络犯罪分子通常会挖掘其中…

1559. 二维网格图中探测环

1559. 二维网格图中探测环 给你一个二维字符网格数组 grid &#xff0c;大小为 m x n &#xff0c;你需要检查 grid 中是否存在 相同值 形成的环。 一个环是一条开始和结束于同一个格子的长度 大于等于 4 的路径。对于一个给定的格子&#xff0c;你可以移动到它上、下、左、右…

【Qt 初识】QPushButton 的详解以及 Qt 中的坐标

文章目录 1. Qt 中的信号槽机制 &#x1f34e;2. 通过图形化界面的方式实现 &#x1f34e;3. 通过纯代码的方式实现按钮版的HelloWorld &#x1f34e;4. 设置坐标 &#x1f34e; 1. Qt 中的信号槽机制 &#x1f34e; 》&#x1f427; 本质就是给按钮的点击操作&#xff0c;关联…

GuLi商城-商品服务-API-品牌管理-OSS获取服务端签名(续)

如何进行服务端签名直传_对象存储(OSS)-阿里云帮助中心 gulimall-third-party服务的代码: package com.nanjing.gulimall.thirdparty.controller;import com.aliyun.oss.OSS; import com.aliyun.oss.OSSClientBuilder; import com.aliyun.oss.common.utils.BinaryUtil; impor…

Linux开发:Fuse介绍

Fuse(filesystem in userspace),是一个用户空间的文件系统。通过fuse内核模块的支持&#xff0c;开发者只需要根据fuse提供的接口实现具体的文件操作时所对应的回调函数&#xff0c;就可以实现一个文件系统。由于其主要实现代码位于用户空间中&#xff0c;因此不需要重新编译内…