[C++]lambda

news2024/11/17 2:24:56

目录

前言:      

1 认识lambda

2 lambda语法

3 lambda的类型

4 lambda的底层


前言:      

        本篇讲解了C++11当中新添加的lambda语法,以及lambda的底层

1 认识lambda

        lambda的出现方便了很多我们写程序的地方,例如下面这个样例,我们对一个自定义类型进行排序,如果没有lambda,我们就只能通过传入函数指针或则是仿函数的方式,如下:

struct Goods
{
	string _name;
	double _price; 
	int _evaluate; 
	Goods(const char* str, double price, int evaluate)
		:_name(str)
		, _price(price)
		, _evaluate(evaluate)
	{}

	void print()
	{
		cout << "name:" << _name << " price: " << _price << "   evaluate:" << _evaluate << endl;
	}
};

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;
	}
};

void printGoods(vector<Goods>& v)
{
	for (auto& e : v)
	{
		e.print();
	}
}

void test1()
{
	ComparePriceGreater compg;
	ComparePriceLess compgl;

	vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };

	//通过仿函数能够达到我们期望的比较方式
	sort(v.begin(), v.end(), compg);
	cout << "价格降序" << endl;
	printGoods(v);
	sort(v.begin(), v.end(), compgl);

	cout << "价格升序" << endl;
	printGoods(v);

}

输出如下:

         上述代码我们描述了一个商品,其中有价格也有评价以及商品名字,用于模拟我们平时看到的电商平台里面的商品,我相信大家脑海中是有相应的样式的,所以大伙一定是知道对于这些商品我们可以通过不同的方式来排序的,上面代码就实现了这个功能。

        我这里采用的是仿函数的方式,可以看到,不管是升序排序还是降序排序我们都分别写了一个仿函数,有时候这个仿函数有很简单,写出这个仿函数感觉有点没有必要,而且有时我们是不想要额外想一个名字的,想名字太痛苦了。所以这个时候就能够用lambda来实现了(注意!我这里只是表示了lambda的场景,并不是说lambda比仿函数更好,实际上它们是同一个级别的东西,后续我会为大家体现的)。

        所以根据上面的代码,我们将仿函数修改成为lambda:

struct Goods
{
	string _name;
	double _price; 
	int _evaluate; 
	Goods(const char* str, double price, int evaluate)
		:_name(str)
		, _price(price)
		, _evaluate(evaluate)
	{}

	void print()
	{
		cout << "name:" << _name << " price: " << _price << "   evaluate:" << _evaluate << endl;
	}
};


void printGoods(vector<Goods>& v)
{
	for (auto& e : v)
	{
		e.print();
	}
	cout << "*************************************" << endl;
}

void test1()
{

	vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };
	//通过lambda语法也能帮助我们完成这个功能
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool { return g1._price < g2._price; });
	printGoods(v);
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool { return g1._price > g2._price; });
	printGoods(v);
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool { return g1._evaluate < g2._evaluate; });
	printGoods(v);
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool { return g1._evaluate > g2._evaluate; });
	printGoods(v);
}

输出如下:

         如上代码所示,我们的代码整体上看起来是不是更加轻松了?直接把lambda作为了sort的比较方式,然后传入。通过这样的方式相信大家也能看出来lambda实际上就是一个匿名函数。

2 lambda语法

        通过上面的介绍,相信大家也见识到了lambda,接下来就来为大家展示一下lambda的语法格式,lambda由如下几个部分组成:

[]:捕捉列表,在lambda函数的最开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,并且这个列表回去捕捉上下文中的变量,然后用于lambda使用。

():参数列表,与函数的参数列表相同,有一点比较特别,那就是如果不需要传递参数,那么不仅不需要写参数如:int x,甚至()都可以不用写。

->returntype:返回值类型,如函数相同,当没有返回值可以不写,当返回值类型非常明确,也可以不写,编译器会推导。

{}:函数体,也就是我们写函数时需要用{}包起来的部分。

mutable:默认条件下,lambda总是有一个const特性,我们可以通过mutable取消掉它的这个特性,并且加上mutable时,()不能省略。

         相信大家看完上面的概念基本上是懵逼的,倒不是说不能理解,而是自己写不出来lambda,接下来就由博主带大家学习。

        根据上面的概念,我们可以得知一个lambda有两个部分必须持有,那就是捕捉列表还有函数体,其余的都可以省略掉,那么我们就可以写下如下的一个最简单的lambda表达式:

         当然这个玩意没啥实际意义,只是博主为了表示lambda最不可或缺的部分。接下来就是lambda的实际使用了,首先我们来实现一个Add函数:

         大家先别管我为什么要用auto来接收lambda,只需要知道lambda是可以通过这种方式被接收的,相当于函数指针typedef一样,使用方式也是如此。只是单纯的看到这一份代码实际上它是没有什么难度的,只要掌握了lambda的写法捕捉列表,参数列表,返回值类型,函数体这样的顺序就行了。

        add函数能够通过这样的方式实现,但是交换函数呢?还能这么写吗?请看代码:

         这样的写法有问题吗?lambda接收x和y的值,然后通过临时对象帮助交换,看起来没有问题,那么输出一下请看?

        看来有问题,lambda好像找不到我们lambda域外面的变量欸,事实上确实是这样,我们可以理解为lambda开辟了一个新的作用域,并且与写它的作用域区分开了,并且我们的传参方式是传参方式,也就是写函数时常见的形参的改变不影响实参,所以有一个很简单的解决方式,如下:

         也就是传引用传参,答案也显而易见,那就是一定能够改变我们的x和y,如下:

         但是除了这样还有什么别的方法吗?还有就是概念当中不是有提到过捕捉列表能够去查找上级作用域的变量供自己使用的吗?这又是怎么用的?别急,代码如下:

         g,这下好咯,不仅x和y不能交换,甚至程序还出现问题了,那么这是什么原因呢?其实原因在输出里面已经为我们提示了,这个lambda是不可以修改的,也就表示了从上级作用域当中找到的变量只有可读的权限,不能修改,这下知道原因了,概念当中的mutable关键字的作用也出来了,那就是让我们的x和y能够被修改,代码如下:

         如上代码,添加了mutable关键字之后,生成可执行程序是不会出错了,那么运行结果会是正确的吗?如下:

         ???,为什么呢?他不是已经找到了那个变量了吗?并且我也添加上了mutable关键字了哇?这怕不是玩我呢?这就涉及到捕捉列表比较特别的部分知识了,那就是捕捉列表当中获取到的数据也是原变量的一个拷贝,如果要使用原数据就需要在这个变量前面添加上&符号,注意这不是取地址的意思,而是引用这个变量,如下:

         输出如下:

         真是不容易,为了搞这么个玩意,不过为了知识,我忍了。

        一下是关于捕捉列表的写法:

    [=]() {};            //全部变量传值传参
    [&]() mutable {};    //全部变量传引用传参
    [=, &x]()mutable {};//接收所有变量,但只有x是引用传参
    [&, x]() mutable {};//接收所有变量,单只有x是传值传参

    this不能直接写,需要在类里面才能使用
    //[this]() {};        //在类里可以通过this,获取到this指针
    class testclass
    {
    public:
        void test100()
        {
            [this]() {
                cout << this << endl;
            }();
        }
    };

3 lambda的类型

        学习了上面的lambda相信大家对于它的使用基本上是没有问题了,那么有个疑问,那就是为什么我的lambda函数要通过auto接收,它的类型是什么样子的?

         请看到如下的代码,明明是两个完全相同的lambda,为什么二者却不能相互赋值呢?事实上,任何一个lambda的类型都不相同,请看如下代码以及输出:

         相信这样的方式,大家就能理解为什么两个代码完全相同的lambda不能相互赋值了,毕竟两个的类型是不同的,我们的lambda有没有支持它们两个的转换,当然就不能够相互转换了。

        但是虽然我们不能通过赋值的方式,但是却可以通过一个lambda去拷贝构造另外一个lambda,如下:

         可以看到,f3是f1的拷贝,它们两个的类型也是一摸一样的,那么这是不是就意味着我们就可以赋值了?

         并不行,而且通过上面的操作,我们也能猜出来lambda底层实现其实就是一个类,因为只有类有拷贝构造以及删除默认函数的操作。

        并且就lambda而言他是可以通过函数指针的方式去接收的,但是由于这种方式在捕捉列表有参数时,是写不出来的,而且通过函数指针的方式接收破坏了lambda的特性我也就不为大家展示了,没有实际的意义。

4 lambda的底层

        讲了这么多,那么lambda到底是怎么实现的?其实很简单,lambda的实现就像是范围for底层实际是迭代器一样,它的底层就是仿函数,别不行,咱现在就给你证明:

class Rate
{
public:
	Rate(double rate) : _rate(rate)
	{}

	double operator()(double money, int year)
	{
		return money * year * _rate;
	}

private:
	double _rate;
};

void test5()
{
	//对象
	double rate = 0.49;
	Rate r1(rate);
	r1(100000, 5);

	//lambda
	auto r2 = [rate](double money, int year)->double {return money * year * rate; };
	r2(100000, 5);

	//不管是对象还是lambda,在底层上面都是先call一个对象,然后再call仿函数,也就是
	//实际上lambda和范围for一样,实际只是底层封装了之后的产物
}

        这份代码我写了一个仿函数还有一个lambda,它们两个实现的是一摸一样的功能,大家学了lambda相信也不需要我解释了,那么我们  调试~ 启动!

        仿函数的汇编代码: 

         lambda的汇编代码:

         请看!有什么区别?唯一的区别那就是仿函数还有lambda的名字不同,其它的都是一模一样的,这下相信了吧。所以说其实lambda其实一点也不难,只要我们知道仿函数,那么一定能理解lambda。


         以上就是博主对于lambda的全部理解了,希望能够帮助到大家。

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

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

相关文章

C++图形开发(2):最基本的图形界面

文章目录 1.构成2.内容介绍2.1 initgraph()2.2 _getch()2.3 closegraph() 3.总结 今天来简单介绍下最基本的图形界面~ 1.构成 输入以下内容并编译&#xff1a; 这就是一个最基本的图形界面了 #include<graphics.h> #include<conio.h>int main() {initgraph(600, …

rollup入门 - 学习笔记(1)

rollup打包工具 打包项目用webpack , 打包js库用rollup 下载rollup npm i rollup --save-dve 初始化项目 npm init -y 创建src/main.js文件 import welcomerollup from ../modules/mymodule welcomerollup(hello world) 创建module/mymodule.js文件 const welcomerollup (ms…

Python PDF生成和二进制流转换(FPDF)

文章目录 安装FPDF生成PDF文档生成PDF的二进制流 安装FPDF pip install fpdf22.7.4生成PDF文档 通过FPDF生成PDF文档的具体步骤&#xff1a; 初始化&#xff1a;fpdf库的操作主要由FPDF对象来处理&#xff0c;在生成PDF文档时&#xff0c;需要初始化FPDF对象。添加页面&…

引爆媒体关注的秘密武器:媒介易教你打造热门新闻故事!

在进行媒体邀约时&#xff0c;提供有吸引力的新闻价值和故事性是吸引媒体关注和获得采访机会的关键。媒体记者时常接收大量的邀约&#xff0c;因此需要与众多企业竞争&#xff0c;才能让自己的邀约脱颖而出。本文将探讨如何在媒体邀约中提供有吸引力的新闻价值和故事性&#xf…

Domain Admin域名和SSL证书过期监控到期提醒

基于Python3 Vue3.js 技术栈实现的域名和SSL证书监测平台 用于解决&#xff0c;不同业务域名SSL证书&#xff0c;申请自不同的平台&#xff0c;到期后不能及时收到通知&#xff0c;导致线上访问异常&#xff0c;被老板责骂的问题 核心功能&#xff1a;域名 和SSL证书 的过期…

【敬伟ps教程】亮度与色阶看懂直方图

文章目录 亮度/对比度色阶调整输入色阶调整输出色阶调节原色通道调整图层 亮度/对比度 控制明暗的视觉因素&#xff0c;三要素之一&#xff0c;明度 在拾色器中修改 HSB 中的 B&#xff0c;改为较低的值即可调整明度。明度较低时&#xff0c;RGB色值偏低&#xff0c;CMYK色值…

怎么看懂今日白银现货价格?这个技术信号了解一下

金叉&#xff0c;是某些技术指标的一种技术动作&#xff0c;通常预示着&#xff0c;今日白银现货价格开始上涨&#xff0c;并且出现了买入机会。但是&#xff0c;是不是金叉就一定有买入机会呢&#xff1f;在今日白银现货价格出现金叉时要如何处理呢&#xff1f;这都是我们要处…

UV统计量

UV统计 UV&#xff1a;Unique Visitor&#xff0c;即独立访客量&#xff0c;是指通过互联网访问、浏览该网页的自然人。一天内同一个用户多次访问该网站&#xff0c;只会记录一次。 PV&#xff1a;Page View&#xff0c;即页面访问量或点击量&#xff0c;用户每访问网站的一个…

d2l_第九章_RNN循环神经网络

x.1 Sequence model 经过前面的学习&#xff0c;我们已知数据大于算法。而以数据为驱动的前提下&#xff0c;我们提出了各种模型。为了适配表格数据&#xff0c;提出了MLP&#xff1b;为了适配图像数据提出了CNN&#xff1b;而对了适配序列数据&#xff0c;我们提出了RNN。 目…

C++笔记之call_once和once_flag

C笔记之call_once和once_flag code review! 文章目录 C笔记之call_once和once_flag1.call_once和once_flag2.std::call_once和单例模式 1.call_once和once_flag 2.std::call_once和单例模式

音频格式转换怎么操作?分享这几个MP3转换器给大家!

有一个名叫小灵的音乐爱好者&#xff0c;对音乐充满热情&#xff0c;每天都沉浸在动听的旋律中&#xff0c;借助耳机享受音乐的魅力。然而&#xff0c;最近她遇到了一个问题&#xff1a;她手头有一些喜欢的音乐文件&#xff0c;但却无法在自己的音乐播放器上播放&#xff0c;这…

【C++】C++教程、学习笔记

文章目录 一、C基础入门1 C初识1.1 第一个C程序1.1.1 创建项目1.1.2 创建文件1.1.3 编写代码1.1.4 运行程序 1.2 注释1.3 变量1.4 常量1.5 关键字1.6 标识符命名规则 2 数据类型2.1 整型2.2 sizeof关键字2.3 实型&#xff08;浮点型&#xff09;2.4 字符型2.5 转义字符2.6 字符…

如何优雅的避免Java中:NullPointerException(空指针异常)

目录 1&#xff1a;空指针问题 2&#xff1a;解决方法 第一种方式&#xff1a; 第二种方式&#xff1a; 第三种方式&#xff1a; 第四种方式&#xff1a; 1&#xff1a;空指针问题 Java是没有指针的&#xff0c;所以我们常说"Java 指针"就是指"Java 的引用…

[RoarCTF 2019]Easy Calc1

看这个输入框&#xff0c;所以我猜测可能是sql注入&#xff0c;弹出了一个对话框&#xff0c;说算不来&#xff0c;说明可能存在过滤 最后发现只要传入字母都会触发弹窗&#xff0c;应该跟题目设定有关系&#xff0c;这只是一个简单的计算器而已 查看了一下源码&#xff0c;发现…

联邦学习 (FL) 中常见的3种模型聚合方法

联邦学习 (FL) 中常见的3种模型聚合方法 联合学习 (FL) 是一种出色的 ML 方法&#xff0c;它使多个设备&#xff08;例如物联网 (IoT) 设备&#xff09;或计算机能够在模型训练完成时进行协作&#xff0c;而无需共享它们的数据。 “客户端”是 FL 中使用的计算机和设备&#x…

Java设计模式之一:装饰器模式

目录 一、什么是装饰器模式 二、装饰器模式如何使用 三、装饰器模式的优势和应用场景 一、什么是装饰器模式 装饰器模式是一种结构型设计模式&#xff0c;允许通过动态地将新功能添加到现有对象上&#xff0c;来扩展其行为或修改其外观&#xff0c;同时不改变其原始类的结构…

模型轻量化神器:3D格式转化工具HOOPS Exchange可将文件压缩小100倍!

​领先的CAD导入和导出库 使用用于CAD数据转换的HOOPS Exchange SDK将30多种CAD文件格式导入到您的应用程序中&#xff0c;具有对2D和3D CAD文件格式&#xff08;包括 CATIA、SOLIDWORKS、Inventor™、Revit™、Creo、 NX™、Solid Edge 等&#xff0c;全部通过单个API实现。 …

TikTok品牌出海:打造独特内容,提升品牌影响力

随着社交媒体的迅猛发展&#xff0c;TikTok作为全球最热门的短视频平台之一&#xff0c;为品牌出海提供了独特的机遇。然而&#xff0c;要在TikTok上成功推广品牌&#xff0c;关键在于创造出引人注目、有吸引力的内容。本文Nox聚星将和大家探讨在TikTok上&#xff0c;什么样的内…

HTML写一个16:9自适应的页面

效果图&#xff1a; 页面可以按照窗口大小自适应变化&#xff0c;并且可以保持16:9 <!DOCTYPE html> <html><head><style>body {margin: 0;padding: 0;display: flex;justify-content: center;align-items: center;height: 100vh;}#box {width: calc(…

APP外包开发的用户行为分析工具

APP开发完成上线后&#xff0c;运营人员希望得到用户在使用APP过程中的行为数据&#xff0c;比如更喜欢哪个功能、停留时长、使用路径等指标数据&#xff0c;这对于产品的下一步改进非常重要。运营和产品人员有了这些数据才能更好的设计新功能。今天和大家分享这方面的工具&…