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表达式 的语法更加直观和灵活,特别是在处理简单函数或回调函数时。它允许开发者直接在表达式中捕获外部变量,并定义函数体,这使得代码更加易于编写和理解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/789463.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

APB总线协议

一、APB总线介绍 关于总线的一些概念&#xff1a; 总线&#xff1a;计算机内部和计算机之间传输数据的共用通道。 总线位宽&#xff1a;总线能够一次性传送的二进制数据位数&#xff0c;例如8bit、16bit、32bit、64bit等。 总线工作频率&#xff1a;即时钟频率&#xff08;时…

PHP实现用户认证与权限管理的全面指南

目录 引言 1. 数据库设计 1.1 用户表&#xff08;users&#xff09; 1.2 角色表&#xff08;roles&#xff09; 1.3 权限表&#xff08;permissions&#xff09; 1.4 用户角色关联表&#xff08;user_roles&#xff09; 1.5 角色权限关联表&#xff08;role_permissions…

【内网渗透】内网渗透学习之域渗透常规方法

域渗透常规方法和思路 1、域内信息收集1.1、获取当前用户信息1.1.1、获取当前用户与域 SID1.1.2、查询指定用户的详细信息 1.2、判断是否存在域1.2、查询域内所有计算机1.3、查询域内所有用户组列表1.4、查询所有域成员计算机列表1.5、获取域密码信息1.6、获取域信任信息1.7、查…

最短路径算法:Dijkstra算法探险记

想象一下,你是一只小蚂蚁,名字叫小明。你住在一个大大的花园里,这个花园有很多小路,小路之间还有交叉点,就像是一个迷宫一样。现在,你接到了一个任务:找到从你家到花园里一个特定地方(比如一块超级大的糖果)的最短路径! 第一步:画出地图 首先,我们需要一张地图来…

YOLOv8改进 | 注意力机制 | 增强模型在图像分类和目标检测BAM注意力【小白必备 + 附完整代码】

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏目录 &#xff1a;《YOLOv8改进有效…

【模块化与包管理】:解锁【Python】编程的高效之道

目录 1.什么是模块&#xff1f; 2. 模块的导入过程 3. 理解命名空间 4. import语句的多种形式 5. 模块的执行与重新导入 6. 包&#xff08;Package&#xff09; 7. sys模块和os模块 sys模块 常用属性 示例&#xff1a;使用sys模块 os模块 常用功能 示例&#xff1…

前端埋点数据收集和数据上报

原文地址 什么是埋点 学名叫时间追踪(Event Tracking), 主要针对用户行为或者业务过程进行捕获&#xff0c;处理和发送相关技术及实施过程. 埋点是数据领域的一个专业术语&#xff0c;也是互联网领域的俗称&#xff0c;是互联网领域的俗称 埋点是产品数据分析的基础&#xf…

【AIGC】一、本地docker启动私有大模型

本地docker启动私有大模型 一、最终效果中英文对话生成代码 二、资源配置三、搭建步骤启动docker容器登录页面首次登录请注册登录后的效果 配置模型尝试使用选择模型选项下载模型选择适合的模型开始下载 试用效果返回首页选择模型中英文对话生成代码 四、附录资源监控 五、参考…

动手学深度学习54 循环神经网络

动手学深度学习54 循环神经网络 1. 循环神经网络RNN2. QA 1. 循环神经网络RNN h t h_t ht​ 与 h t − 1 h_{t-1} ht−1​ x t − 1 x_{t-1} xt−1​有关 x t x_t xt​ 与 h t h_t ht​ x t − 1 x_{t-1} xt−1​ 有关 怎么把潜变量变成RNN–假设更简单 潜变量和隐变量的区…

Java面试八股之什么是布隆过滤器

什么是布隆过滤器 布隆过滤器&#xff08;Bloom Filter&#xff09;是一种空间效率极高的概率型数据结构&#xff0c;用于判断一个元素是否可能存在于一个集合中。布隆过滤器可以给出“可能存在”或“一定不存在”的答案&#xff0c;但不能保证“一定存在”。其主要特点是&…

WTM的项目中EFCore如何适配人大金仓数据库

一、WTM是什么 WalkingTec.Mvvm框架&#xff08;简称WTM&#xff09;最早开发与2013年&#xff0c;基于Asp.net MVC3 和 最早的Entity Framework, 当初主要是为了解决公司内部开发效率低&#xff0c;代码风格不统一的问题。2017年9月&#xff0c;将代码移植到了.Net Core上&…

开源项目有哪些机遇与挑战

目录 1.概述 2.开源项目的发展趋势 2.1. 开源项目的发展现状 2.2. 开源社区的活跃度 2.3. 开源项目在技术创新中的作用 3.参与开源的经验分享 3.1. 选择开源项目 3.2. 理解项目结构和文档 3.3. 贡献代码 3.4. 与开源社区的合作 3.5. 学习和成长 4.开源项目的挑战 …

buuctf 二维码

文件下载下来是一个png的文件 做misc永远的好习惯就是先运行,后010 先运行,这个运行肯定就是扫码 啥也没有 里面还有个ZIP文件(zip的发明人名字是PK) 放在kali上binwalk分离 CTF工具隐写分离神器Binwalk安装和详细使用方法_binwalk下载-CSDN博客 里面有个text,需要密码 我…

ESP32驱动摄像头:1.驱动OV2640模块(待验证)

一、装ArduCam库和ESPAsyncWebServer库 二、参考代码 #include <Wire.h> #include <ArduCAM.h> #include <SPI.h> #include <WiFi.h> #include <ESPAsyncWebServer.h>#define CAM_CS 32 // modify according to your own wiring #define OV2640…

IP 地址:优化网络游戏

IP地址和网络游戏 在现代网络游戏中&#xff0c;IP地址不仅用于服务器分配&#xff0c;还能针对性进行玩家匹配与优化网络延迟。本文将探讨IP地址在网络游戏中的具体应用。 *服务器分配* 全球服务器分布&#xff1a; 网络游戏需要在全球范围内提供快速、稳定的连接&#xff…

【机器学习】主成分分析(PCA):数据降维的艺术

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 主成分分析&#xff08;PCA&#xff09;&#xff1a;数据降维的艺术引言PCA的基…

【TypeScript 学习】TypeScript 枚举类型发散:基于位运算的权限管理 CRUD 操作

文章目录 TypeScript 枚举类型发散&#xff1a;基于位运算的权限管理 CRUD 操作1 问题由来2 具体实现2.1 新增权限2.2 删除权限2.3 查询权限&#xff08;即判定存在与否&#xff09;2.4 修改权限2.5 完整测试 3 小结 TypeScript 枚举类型发散&#xff1a;基于位运算的权限管理 …

鸿蒙开发:Universal Keystore Kit(密钥管理服务)【加解密(C/C++)】

加解密(C/C) 以AES 256密钥为例&#xff0c;完成加解密。具体的场景介绍及支持的算法规格。 在CMake脚本中链接相关动态库 target_link_libraries(entry PUBLIC libhuks_ndk.z.so)开发步骤 生成密钥 指定密钥别名。初始化密钥属性集。调用OH_Huks_GenerateKeyItem生成密钥)…

[Linux安全运维] Linux用户以及权限管理

Linux用户以及权限管理 Linux用户和组 用户信息文件pasawd /etc/passwd文件用于存储用户的信息 :用于分割不同的字段信息 字段示例&#xff08;第一行&#xff09;含义说明1root用户名2x密码占位符x代表用户有密码存储在shadow文件中无内容代表用户登录系统不需要密码30UID…

一款24小时实时检测的六氟化硫气体泄漏报警系统

尽管当前工业生产模式越来越趋于自动化、智能化&#xff0c;但安全生产仍然是时下屡被提及的话题。在配电室等使用六氟化硫气体的众多领域中&#xff0c;由于气体泄漏而引发的中毒、火灾、爆炸、窒息事故仍高发频发。因此&#xff0c;安装六氟化硫气体泄漏报警监测系统仍是企业…