【C++】C++11语法 ~ lambda 表达式

news2025/6/8 21:02:30

🌈欢迎来到C++专栏~~ lambda 表达式


  • (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是Scort
  • 目前状态:大三非科班啃C++中
  • 🌍博客主页:张小姐的猫~江湖背景
  • 快上车🚘,握好方向盘跟我有一起打天下嘞!
  • 送给自己的一句鸡汤🤔:
  • 🔥真正的大师永远怀着一颗学徒的心
  • 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏
  • 🎉🎉欢迎持续关注!
    在这里插入图片描述

请添加图片描述

文章目录

  • 🌈欢迎来到C++专栏~~ lambda 表达式
    • 一. 概念
    • 二. 语法
    • 三. 捕获方式
      • 💥相互赋值
    • 四. 底层实现
    • 五. mutable(作用不大)
  • 📢写在最后

请添加图片描述

一. 概念

自 C++11 开始,C++ 有三种方式可以像函数使用的对象 / 类型:

函数指针
仿函数
Lambda 表达式

此处要注意:greater后面的括号是什么时候要加上?(容易混淆)

  • 首先我们要清楚,传的是对象还是类型:传的是对象就要加(),如果是类型就不用

在这里插入图片描述
言归正传,lambda 表达式本质上就是一个匿名函数

这里举个例子:

struct Items
{
	string _name;  //名字
	double _price; //价格
	int _num;      //数量
};

如果要对若干对象分别按照价格和数量进行升序、降序排序

可以使用 sort 函数,但由于这里待排序的元素为自定义类,如果想按照我们的数据进行排序,只能通过仿函数来实现了,那岂不是要实现6个仿函数?
有点麻烦了

struct ComparePriceLess//价格降序
{
	bool operator()(const Goods& g1, const Goods& g2)
	{
		return g1._price < g2._price;
	}
};
struct ComparePriceGreater//价格升序
{
	bool operator()(const Goods& g1, const Goods& g2)
	{
		return g1._price > g2._price;
	}
};
int main()
{
	vector<Goods> v = { { "苹果", 2.1, 300 }, { "香蕉", 3.3, 100 }, { "橙子", 2.2, 1000 }, { "菠萝", 1.5, 1 } };
	sort(v.begin(), v.end(), ComparePriceLess());    //价格升序
	sort(v.begin(), v.end(), ComparePriceGreater()); //价格降序
	return 0;
}

为此lambda 表达式就横空出世了

二. 语法

lambda 表达式定义:

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

表达式各部分说明:

  • [capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用
  • (parameters)参数列表与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略(无参可以省略
  • mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量
    性。使用该修饰符时,参数列表不可省略(即使参数为空)
  • ->returntype返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导
  • {statement}函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量

除了捕获列表,Lambda表达式的其它地方其实和普通的函数基本一样,lambda 参数列表和返回值类型都是可有可无的,但捕捉列表和函数体是不可省略的,因此最简单的lambda函数如下:

int main()
{
	[]{}; //最简单的lambda表达式
	return 0;
}

再举个函数相加的例子:

int main()
{
	//两个函数相加
	auto add1 = [] (int a, int b)->int{return a + b; };
	cout << add1(1, 2) << endl;

	//省略返回值
	auto add2 = [](int a, int b){return a + b; };
	cout << add1(1, 2) << endl;

	return 0;
}

如果我们要不传参数,该怎么样实现呢?

三. 捕获方式

Lambda 表达式最基本的两种捕获方式是:按值捕获和按引用捕获

  • [var]:值传递捕捉变量var
  • [=]:值传递捕获所有父作用域中的变量(成员函数包括this指针)
  • [&var]:引用传递捕捉变量var
  • [&]:引用传递捕捉所有父作用域中的变量(成员函数包括this指针)

注意:

  1. 1️⃣父作用域要包含lambda函数语句(一般指的是当前所在的函数(栈帧)
  2. 2️⃣语法上捕捉可由多个捕捉项组成,并以逗号分割(就是可以混合着来)
  //混合捕捉:a是引用捕捉,其他都是传值捕捉
  auto f1 = [=, &a]() {
  	cout << a << b << c << d << e << endl;
  };

  f1();
  1. 3️⃣捕捉列表不允许变量重复传递,否则就会导致编译错误
  2. 4️⃣在块作用域以外的lambda函数捕捉列表必须为空
  3. 5️⃣在块作用域中的lambda函数仅能捕捉父作用域中局部变量,捕捉任何非此作用域或者非局部变量都 会导致编译报错。

来个例题:此处的f可以++吗?

int f = 0;
int main()
{
	int a, b, c, d, e;
	a = b = c = d = e = 1;

	//混合捕捉:a是引用捕捉,其他都是传值捕捉
	auto f1 = [=, &a]() {
		f++;
		cout << a << b << c << d << e << endl;
	};

	f1();
	return 0;
}

f为什么可以呢?

  • 因为f全局变量不存在于栈帧里,存在于静态区,哪个位置都可以用它!

复习一下:
对象的作用域和存储区域要分清楚:

  • 生命周期是和存储的区域有关系
  • 作用域(编译器编译,用的地方能否找到) 局部 —> 全局

💥相互赋值

lambda表达式之间不能相互赋值,就算是两个一模一样的也不行

lambda 表达式会被处理为函数对象,该函数对象对应的类名叫做<lambda_uuid>

类名中的uuid叫做通用唯一识别码,简单来说就是通过算法生成的一串字符串,它具有随机性和不重复性,保证在当前程序中每次生成不同的 uuid,因为 lambda 表达式底层的类名包含 uuid,这就保证了每个 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表达式赋值给相同类型的函数指针

就是在我们看来是一样的,但是其底层大有不同!

四. 底层实现

实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的,就是对()进行了重载

class Add
{
public:
	Add(int base)
		:_base(base)
	{}
	int operator()(int num)
	{
		return _base + num;
	}
private:
	int _base;
};
int main()
{
	int base = 1;

	//函数对象
	Add add1(base);
	add1(1000);

	//lambda表达式
	auto add2 = [base](int num)->int
	{
		return base + num;
	};
	add2(1000);
	return 0;
}

对反汇编进行观察:

在这里插入图片描述

当创建add对象的时候是构造函数,使用add对象的时候就是会调用 Add 类的 () 运算符重载函数

在这里插入图片描述

lambda 表达式同样如此:会调用 <lambda_uuid> 类的构造函数,在使用add2对象时,会调用<lambda_uuid>类的 ()运算符重载函数

其本质就是:lambda表达式在底层被转换成了仿函数

当我们定义一个lambda表达式后,编译器会自动生成一个类,在该类中对 () 运算符进行重载,实际 lambda 函数体的实现就是这个仿函数 operator() 的实现,在调用 lambda 表达式时,参数列表和捕获列表的参数,最终都传递给了仿函数的 operator()

五. mutable(作用不大)

在实际使用中,比如实现一个交换函数,我们用 lambda 表达式实现:

int main()
{
	auto swap2 = [x, y]() 
	{
		int tmp = x;
		x = y;
		y = tmp;
	};
	swap2();
	return 0;
}

这里我们发现是传值传参!果然编译不通过,因为传值捕获到的变量默认是不可修改的(const):

如果要取消其常量属性,就需要在 lambda 表达式中加上 mutable 像这样:

	auto swap2 = [x, y]() mutable    //改变的是形参,实参无影响,所以没用
	{
		int tmp = x;
		x = y;
		y = tmp;
	};

但是捕捉列表是传值捕捉过来的,不影响外面的实参;所以这种方法无法完成交换功能

在这里插入图片描述

📢写在最后

请添加图片描述

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

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

相关文章

WPF常用UI库和图标库(MahApps、HandyControl、LiveCharts)

WPF常用UI库和图表库&#xff08;MahApps、HandyControl、LiveCharts&#xff09; WPF有很多开源免费的UI库&#xff0c;本文主要介绍常见的MahApps、HandyControl两个UI库&#xff1b;在开发过程中经常会涉及到图表的开发&#xff0c;本文主要介绍LiveCharts开源图表库。 UI…

Dell Precision T7910 工作站做RAID

1&#xff1a;开机根据提示按Ctrl-C 2&#xff1a;进入下面界面直接按回车。Adapter是LSISAS3008IR的卡。 3&#xff1a;回车来到下面的界面&#xff0c;我们选择RAID Propertie回车。 4&#xff1a;回车来到选择RAID级别的界面。根据自己的硬盘数量和需求进行选择。 5&#xf…

云原生丨Prometheus+Grafana监控 OpenGauss 数据库

文章目录前言一、Prometheus的介绍及安装1、Prometheus 介绍2、Prometheus 安装二、Grafana的介绍及安装1.Grafana 介绍2、Grafana 安装三、安装探针1、安装Node Exporter探针2.安装opengauss_exporter探针四、 访问Prometheus与Grafana1、 访问Prometheus2、 访问 Grafana五、…

React 组件性能优化

React 组件性能优化1. 组件卸载前进行清理操作2. PureComponent3. shouldComponentUpdate4. React.memo5. 使用组件懒加载6. 使用 Fragment 避免额外标记7. 不要使用内联函数定义8. 在构造函数中进行函数this绑定9. 类组件中的箭头函数10. 避免使用内联样式属性11. 优化条件渲染…

记录复现一下第一次awd

前言 之前没打过awd&#xff0c;这次学长组织了一场awd娱乐赛&#xff0c;两个web一个pwn&#xff0c;还有一个黑盒&#xff0c;只会web&#xff0c;第一次啥也不会瞎打&#xff0c;被打烂了&#xff0c;不会写脚本&#xff0c;手交flag的感觉真“不错”&#xff0c;感觉awd还…

NetIQ 高级认证框架

NetIQ 高级认证框架 NetIQ Advanced Authentication 提供无密码身份验证并提升安全访问&#xff0c;以满足这个可扩展的基于标准的身份验证框架的合规要求。 优点 1、灵活性不仅仅在于方法。平台和应用程序支持至关重要。将安全范围扩展到您的所有系统。 2、通过一套适合…

大数据技术之Hadoop(生产调优手册)

第1章 HDFS—核心参数 1.1 NameNode内存生产配置 1&#xff09;NameNode内存计算 每个文件块大概占用150byte&#xff0c;一台服务器128G内存为例&#xff0c;能存储多少文件块呢&#xff1f; 128 * 1024 * 1024 * 1024 / 150Byte ≈ 9.1亿 G MB KB Byte 2&#xff09;Hadoop…

百度笔记聚合怎么写

百度笔记聚合怎么写&#xff0c;#百度笔记聚合&#xff0c;#百度笔记优化&#xff0c;#百度笔记排名 小红书笔记收录大揭秘什么是笔记被收录&#xff1f; 你将你的笔记的标题复制&#xff0c;去搜索框搜索&#xff0c;如果能搜索到你的笔记出来&#xff0c;那就是被收录了。什…

C语言学习笔记-文件读写

C 文件读写 什么是文件&#xff1f; 文件是以计算机硬盘为载体存储在计算机上的信息集合。是数据源的一种&#xff0c;最主要的作用是保存数据。在程序设计中&#xff0c;我们可将文件分为两大类&#xff1a;程序文件和数据文件 &#xff08;1&#xff09;程序文件 包括源程…

浅谈前端安全和浏览器安全策略

前端安全 XSS(跨站脚本攻击) Cross-Site Scripting&#xff08;跨站脚本攻击&#xff09;简称 XSS&#xff0c;是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本&#xff0c;使之在用户的浏览器上运行。利用这些恶意脚本&#xff0c;攻击者可获取用户的敏感信息如 Co…

208:vue+openlayers 监听瓦片地图加载情况,200、403及其他状态码的处理示例

第208个 点击查看专栏目录 本示例的目的是介绍如何在vue+openlayer中加载XYZ地图,在load瓦片时,通过XMLHttpRequest进行捕捉,监听瓦片地址的loadend事件,针对不同的状态码,给出不同的处理结果。具体的方法参看源代码。 直接复制下面的 vue+openlayers源代码,操作2分钟即…

Conda安装到虚拟环境中的包在pycharm中不显示--pip下载的包都到了base环境中-Ubuntu20.04

问题 今天刚装了一个Ubuntu20.04系统&#xff0c;安装完Anaconda&#xff0c;虚拟环境的包也都下载好了&#xff0c;结果在pycharm中配置完解释器后&#xff0c;只有几个基础的包&#xff0c;切换到base环境后发现&#xff0c;这些包都被下载到了base环境中。 在网上查了各种…

《小猫猫大课堂》三轮5——动态内存管理(通讯录动态内存化)

宝子&#xff0c;你不点个赞吗&#xff1f;不评个论吗&#xff1f;不收个藏吗&#xff1f; 最后的最后&#xff0c;关注我&#xff0c;关注我&#xff0c;关注我&#xff0c;你会看到更多有趣的博客哦&#xff01;&#xff01;&#xff01; 喵喵喵&#xff0c;你对我真的很重…

seata分布式事务之AT模式实践代码

seata框架 AT模式&#xff1a; 先添加seata需要的数据库相关表&#xff0c;AT模式需要在每个业务所属库下建undo_log表&#xff0c;用来回滚的&#xff0c;出错seata就会从这个表生成反向sql回退数据 建表语句&#xff1a; SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0; -…

持续集成Jenkins (四)Jenkins+git+maven项目构建、自动化部署

GIT配置1.1 前言&#xff1a;需要安装 git 客户端.yum install git1.2 Jenkins 配置插件 Git 在仪表盘选择Manage Jenkins>>Plugin Manager>>进入如下页面&#xff0c;可以选择可选的插件&#xff0c;安装完成后的插件在installed里面可以看到&#xff0c;我这里已…

DataFrame与Spark SQL的由来

文章目录DataFrame与Spark SQL的由来RDD 之殇&#xff1a;优化空间受限DataFrame 横空出世幕后英雄&#xff1a;Spark SQL基于 DataFrame&#xff0c;Spark SQL 是如何进行优化的Catalyst 优化器TungstenDataFrame与Spark SQL的由来 Spark 已经有了 RDD 这个开发入口&#xff…

市场最快图表:LightningChart .NET v.10.4.1 Crack

LightningChart .NET v.10.4.1 已经发布&#xff01; 新功能、新自定义控件和性能改进 DataCursor&#xff1a;图表中自动数据跟踪的新功能。 在以前的版本中&#xff0c;LightningChart .NET 提供了不同的工具来实现数据跟踪功能&#xff0c;但是这些需要用户进行一些额外的编…

Python-第一天 安装Python和PyCharm

Python-第一天 安装Python和PyCharm一、安装Python1. 下载2. 安装3.验证是否安装成功二、安装和配置PyCharm工具1.下载2.安装3.创建工程4.配置4.1 修改主题4.2 修改默认字体和大小4.3 通过快捷键快速设置字体大小4.4 汉化软件4.5 其它插件4.6 常用快捷建一、安装Python 1. 下载…

计算机图形学02:中点BH算法绘制直线

作者&#xff1a;非妃是公主 专栏&#xff1a;《计算机图形学》 博客地址&#xff1a;https://blog.csdn.net/myf_666 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁&#xff0c;以心制境&#xff0c;万事可成。——曾国藩 文章目录专栏推荐专栏系列文章序一、算法原理二、缺…

计算机科学基础知识第二节讲义

课程链接 运行环境&#xff1a;WSL Ubuntu OMZ终端 PS&#xff1a;看到老师终端具有高亮和自动补全功能&#xff0c;我连夜肝出oh-my-zsh安装教程&#xff0c;实现了此功能。 这节课主要讲变量的语法、控制流程、shell功能等内容。 修改终端用户名&#xff0c;输入密码后重启…