C++_lambda表达式

目录

前言-lambda表达式的介绍:

1、lambda表达式的作用

2、lambda表达式的优势

2.1 用lambda构建lambda变量

3、lambda表达式的类型

4、捕捉列表说明 

4.1 传值捕捉 

4.2 mutable 

4.3 传引用捕捉 

4.4 混合捕捉 

5、lambda的大小

结语 


前言-lambda表达式的介绍:

        C++11推出lambda表达式,lambda表达式实际上是一个匿名函数,他的具体书写格式如下:

        lambda格式:[capture-list] (parameters) mutable -> return-type { statement  }

        1、[capture-list]表示捕捉列表,他是lambda函数的标志性符号,编译器会根据[]来判断该式子是否为lambda函数,他的另一个作用是可以捕捉父类作用域的变量给到lambda函数使用,因此叫捕捉列表。

        2、(parameters)表示lambda的参数列表,他的用法和意义跟普通函数的形参列表是一样的,如果不需要对lambda传任何参数则可以连同括号一起省略。

        3、mutable的作用是消除捕捉列表中的常属性,因为lambda的捕捉列表中的变量都是默认被const修饰的,因此lambda函数内部是不能修改[capture-list]中的参数,但是加上mutable后就可以在函数体内部修改捕捉的参数了。注意:如果加上mutable则参数列表(parameters)必须有参数,即使参数列表为空。

        4、-> return-type表示lambda函数的返回值类型,他的意义等同于普通函数的返回类型,只不过在lambda函数中可以省略-> return-type(不管lambda函数有没有返回值都可以省略),由编译器自动推导该lambda函数的返回值类型。

        

        5、{ statement  }表示lambda的函数体,因为lambda实际上是一个匿名函数,所以他也有属于自己的函数体,并且在捕捉列表中的变量可以在该函数体内使用。

        从以上叙述可以得到一个有趣的写法,因为lambda表达式中只有捕捉列表和函数体的标识符不能省略,因此最简单的lambda函数可以写为:[]{},该lambda函数没有实现任何的功能。

1、lambda表达式的作用

        为什么会C++11会推出lambda表达式呢?因为在此之前,我们如果要自己实现一个仿函数则需要手写一个类,如果要实现的逻辑复杂而多样,则要实现多个类进行仿函数的调用,类一旦多了起来就会有重名的烦恼,并且实现的类的目的仅仅是为了的仿函数调用,难免会感到大材小用,比如以下示例: 

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

struct Shop
{
	string _name;//商品名字
	double _price;//价格
	int _evaluate; // 评价
	Shop(const char* str, double price, int evaluate)
		:_name(str)
		, _price(price)
		, _evaluate(evaluate)
	{}
	friend ostream& operator<<(ostream& out, const Shop& s);
	
};
ostream& operator<<(ostream& out, const Shop& s)//流提取
{
	cout << s._name << " " << s._price << " " << s._evaluate << endl;
	return out;
}
struct ComparePriceLess//价格低的优先
{
	bool operator()(const Shop& gl, const Shop& gr)
	{
		return gl._price < gr._price;
	}
};
struct ComparePriceGreater//价格高的优先
{
	bool operator()(const Shop& gl, const Shop& gr)
	{
		return gl._price > gr._price;
	}
};
int main()
{
	vector<Shop> v = { { "苹果", 12.2, 5 }, { "香蕉", 3.3, 4 }, { "橙子", 6.6,
   3 }, { "菠萝", 1.5, 4 } };
	sort(v.begin(), v.end(), ComparePriceLess());
	//打印
	for (auto num : v)
	{
		cout << num;
	}
	cout << endl;
	sort(v.begin(), v.end(), ComparePriceGreater());
	//打印
	for (auto num : v)
	{
		cout << num;
	}
	cout << endl;
}

        运行结果:

        从结果可以看到,虽然创建类调用仿函数可以有效的实现我们的要求,但是排序的逻辑不同就要再多写一个类来调用仿函数, 人们觉得该写法过于复杂,因此推出lambda表达式来代替以上的写法。

2、lambda表达式的优势

        比如以上代码,若用lambda表达式代替仿函数的类,优化后代码如下: 

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

struct Shop
{
	string _name;//商品名字
	double _price;//价格
	int _evaluate; // 评价
	Shop(const char* str, double price, int evaluate)
		:_name(str)
		, _price(price)
		, _evaluate(evaluate)
	{}
	friend ostream& operator<<(ostream& out, const Shop& s);

};
ostream& operator<<(ostream& out, const Shop& s)//流提取
{
	cout << s._name << " " << s._price << " " << s._evaluate << endl;
	return out;
}

int main()
{
	vector<Shop> v = { { "苹果", 12.2, 5 }, { "香蕉", 3.3, 4 }, { "橙子", 6.6,
   3 }, { "菠萝", 1.5, 4 } };

	//sort(v.begin(), v.end(), ComparePriceLess());
	sort(v.begin(), v.end(), 
	[](const Shop& gl,const Shop& gr)->bool {return gl._price < gr._price; });

	//打印
	for (auto num : v)
	{
		cout << num;
	}
	cout << endl;

	//sort(v.begin(), v.end(), ComparePriceGreater());
	sort(v.begin(), v.end(), 
	[](const Shop& gl, const Shop& gr)->bool {return gl._price > gr._price; });

	//打印
	for (auto num : v)
	{
		cout << num;
	}
	cout << endl;
}

        运行结果:

        从结果可以发现,用lambda表达式可以完美的替代类调用仿函数,并且在代码量上也得到了显著的减少。

2.1 用lambda构建lambda变量

        以上虽然是lambda函数的用法之一,但是lambda函数习惯先给到一个变量,然后再用该变量去调用lambda函数,因为这样写可以提高代码的可读性。

        示例如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

int main()
{
	//构建lambda对象add
	//auto add = [](int x, int y)->int {return x + y; };
	auto add = [](int x, int y) {return x + y; };//省略返回值的写法

	int sum = add(10, 20);//用lambda对象add去调用lambda函数
	cout << sum << endl;

	//若直接用lambda表达式调用则可读性不高
	cout << [](int x, int y) {return x + y; }(13, 13) << endl;

	return 0;
}

        运行结果:

3、lambda表达式的类型

        lambda构建的变量虽然看起来都是lambda类型,但是他们相互之间不能够直接赋值,因为在底层,每个lambda构建的变量的类型都存在细微的差异。

        示例代码如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

int main()
{
	auto st1 = [] {cout << "hello world1" << endl; };
	auto st2 = [] {cout << "hello world2" << endl;};

	//st1 = st2;//lambda对象之间不能够相互赋值

	auto st3(st1);//但是可以拷贝构造一个新的lambda变量
	st3();

	//lambda可以赋值返回值类型相同的函数指针
	void(*pf)();
	pf = st2;
	pf();
	return 0;
}

         运行结果:

4、捕捉列表说明 

        如下图所示,虽然lambda表达式在main函数中,但是lambda表达式的函数体中不能直接用main函数的变量:

4.1 传值捕捉 

        捕捉列表可以捕捉父类作用域里的变量(也就是上述的main作用域的变量),并且可以在lambda表达式中用到捕捉的变量,写法如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

int main()
{
	int x = 1, y = 2;
	auto swap = [x, y]//此处省去了括号()
	{
		cout << x << " " << y << endl;
	};
	swap();
	return 0;
}

        运行结果:

        默认写法的捕捉方式均为传值捕捉,传值捕捉的写法还可以用一个‘=’号来表示,即以传值方式捕捉外部父作用域的所有变量,代码如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

int main()
{
	int x = 1, y = 2;
	auto swap = [=]//'='号表示以传值方式捕捉外部所有变量
	{
		cout << x << " " << y << endl;
	};
	swap();
	return 0;
}

        以上的传值捕捉,即对x和y的改变不会影响main函数中的x和y,但是我们发现此时不能直接对捕捉的x和y进行修改:

        因为lambda的捕捉列表中的变量都是默认被const修饰的,因此lambda函数内部是不能修改[capture-list]中的参数,这时候要用关键字mutable才可以对捕捉数据进行修改。 

4.2 mutable 

         mutable的作用是消除捕捉列表中的常属性,具体写法如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

int main()
{
	int x = 1, y = 2;
	auto swap = [=]()mutable//mutable
	{
		x++;
		y++;
		cout << x << " " << y << endl;
	};
	swap();
	cout << x << " " << y << endl;
	return 0;
}

        运行结果:

        而且从上面代码中可以发现,之前都省略了括号‘()’,但是如果用到了mutable关键字后,即使参数列表中没有参数也不能省略括号‘()’。

        并且从运行结果可以发现,传值捕捉是无法更改外部x和y的值,若想更改外部的x和y需用传引用捕捉。

4.3 传引用捕捉 

        传引用捕捉的符号为:‘&’,他也有两层用法,一是直接在捕捉列表中写&,表示以传引用的形式捕捉父作用域所有变量,二是对要捕捉的变量前面加上&。

        具体示例代码如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

int main()
{
	int x = 1, y = 2;
	cout << x << " " << y << endl;
	//auto swap = [&x, &y]
	auto swap = [&]
	{
		int temp = x;
		x = y;
		y = temp;
	};
	swap();
		cout << x << " " << y << endl;
	return 0;
}

        运行结果:

        从结果可以发现,lambda内部对x和y进行修改会影响外部的x和y(并且传引用捕捉不需要mutable也能改变捕捉变量的值)。 

        捕捉列表总结:

        1、[var]:表示传值方式捕捉变量 var
        2、[=]:表示传值方式捕获所有父作用域中的变量( 包括 this指针)
        3、[&var]:表示传引用方式捕捉变量 var
        4、[&]:表示传引用方式捕捉所有父作用域中的变量( 包括 this指针)

4.4 混合捕捉 

        传值捕捉和传引用捕捉两种写法可以相互交错使用,把这样的使用方式叫做混合捕捉,比如以下代码:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

int main()
{
	int x = 1, y = 2;
	cout << x << " " << y << endl;
	auto swap = [&,y]()mutable//除了y是传值捕捉,其他都是传引用捕捉
	{
		x++;
		y++;
	};
	swap();
		cout << x << " " << y << endl;
	return 0;
}

         运行结果:

        可以从结果看到,x是传引用捕捉,因此外面x的值发生了变化,而y是传值捕捉,则修改内部的x不会影响外部的y。 

5、lambda的大小

        因为底层还是会把lambda表达式当成一个仿函数的类去处理,所以lambda表达式中的参数列表会作为类中operator=()运算符重载的参数列表,lambda的函数体对应operator=()的函数体,而捕捉列表就对应的是类的成员。

        当捕捉列表什么都没有捕捉的时候,lambda的大小为1(表示对象的占位),示例代码如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

int main()
{
	int x = 1, y = 2;
	auto swap = [](int x,int y)mutable
	{
		int temp = x;
	};
	cout << sizeof(swap) << endl;
	return 0;
}

         运行结果:


        当lambda的捕捉列表中有捕捉的数据,则对标仿函数的类中含有成员变量的情况, 代码如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

int main()
{
	int x = 1, y = 2;
	auto swap = [y](int x,int y)mutable//捕捉了y
	{
		int temp = x;
	};
	cout << sizeof(swap) << endl;
	return 0;
}

        运行结果:

结语 

         以上就是关于lambda表达式的讲解,lambda实际上是为了避免程序员写仿函数时需要写大量的类而提出的一个新语法,本来让程序员实现的仿函数的类交给由编译器去完成,减轻了程序员的工作。

        最后希望本文可以给你带来更多的收获,如果本文对你起到了帮助,希望可以动动小指头帮忙点赞👍+关注😎+收藏👌!如果有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!! 

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

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

相关文章

C语言-写一个简单的Web服务器(一)

基于TCP的web服务器 概述 C语言可以干大事&#xff0c;我们基于C语言可以完成一个简易的Web服务器。当你能够自行完成web服务器&#xff0c;你会对C语言有更深入的理解。对于网络编程&#xff0c;字符串的使用&#xff0c;文件使用等等都会有很大的提高。 关于网络的TCP协议在…

python编程从入门到实践答案二

python编程从入门到实践 第五章 if语句1.条件测试&#xff1a;2.更多的条件测试&#xff1a;3.外星人颜色#1&#xff1a;4. 外星人颜色#2&#xff1a;5. 外星人颜色#3&#xff1a;6. 人生的不同阶段&#xff1a;7. 喜欢的水果&#xff1a;8. 以特殊方式跟管理员打招呼&#xff…

探索CSS预处理器:Sass、Less与Stylus

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

【Python】进阶学习:列表推导式如何使用两个for循环

【Python】进阶学习&#xff1a;列表推导式如何使用两个for循环 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448; 希望得到…

Linux多线程之线程互斥

(&#xff61;&#xff65;∀&#xff65;)&#xff89;&#xff9e;嗨&#xff01;你好这里是ky233的主页&#xff1a;这里是ky233的主页&#xff0c;欢迎光临~https://blog.csdn.net/ky233?typeblog 点个关注不迷路⌯▾⌯ 目录 一、互斥 1.线程间的互斥相关背景概念 2.互…

canvas实现水印逻辑分析

目录 效果图一、相关文档二、分析三、实现1、将水印文字转为水印图片2、给刚生成的水印图片加入旋转以及间隔&#xff08;1&#xff09;旋转位移&#xff08;2&#xff09;间隔位移&#xff08;3&#xff09;最后使用toDataURL导出为png图片 3、将生成的水印图片依次排布在需要…

Python实现一个简单的主机-路由器结构(计算机网络)

说明 本系统模拟实现了一个路由器与两个主机节点。该路由器将接收原始以太网帧&#xff0c;并像真正的路由器一样处理它们&#xff1a;将它们转发到正确的传出接口&#xff0c;处理以太网帧&#xff0c;处理 IPv4 分组&#xff0c;处理 ARP分组&#xff0c;处理 ICMP 分组&…

Crow 编译和环境搭建

Crow与其说是编译&#xff0c;倒不如说是环境搭建。Crow只需要包含头文件&#xff0c;所以不用编译生成lib。 Crow环境搭建 boost&#xff08;可以不编译boost&#xff0c;只需要boost头文件即可&#xff09;asio &#xff08;可以不编译&#xff0c;直接包含头文件。不能直接…

事务【MySQL】

稍等更新图片。。。。 事务的概念 引入 在 A 转账 100 元给 B 的过程中&#xff0c;如果在 A 的账户已经减去了 100 元&#xff0c;B 的账户还未加上 100 元之前断网&#xff0c;那么这 100 元将会凭空消失。对于转账这件事&#xff0c;转出和转入这两件事应该是绑定在一起的…

C语言——函数指针——函数指针变量(详解)

函数指针变量 函数指针变量的作用 函数指针变量是指向函数的指针&#xff0c;它可以用来存储函数的地址&#xff0c;并且可以通过该指针调用相应的函数。函数指针变量的作用主要有以下几个方面&#xff1a; 回调函数&#xff1a;函数指针变量可以作为参数传递给其他函数&…

Docker基础教程 - 10 常用容器部署-Redis

更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 10 常用容器部署-Redis 下面介绍一下常用容器的部署。可以先简单了解下&#xff0c;用到再来详细查看。 在 Docker 中部署 Redis 容器。 10.1 搜索镜像 首先搜索镜像&#xff0c;命令&…

强大的项目管理软件:OmniPlan Pro 4 mac中文版

OmniPlan Pro 4 mac中文版是由The Omni Group为macOS和iOS操作系统开发的一款专业级项目管理软件。它允许用户创建和管理复杂的项目&#xff0c;从定义任务、分配资源到跟踪进度和生成报告&#xff0c;一应俱全。 这款软件提供了一系列强大的工具&#xff0c;帮助用户进行高效…

集合框架(一)Set系列集合

Set<E>是一个接口 特点 无序&#xff1a;添加数据的顺序和获取出的数据顺序不一致&#xff1b;不重复&#xff0c;无索引 注意&#xff1a;Set要用到的常用方法&#xff0c;基本上就是collection提供的!自己几乎没有额外新增一些常用功能! HashSet集合的底层原理 前置知…

GPU 和并行计算

还是那句话&#xff0c;互联网领域遇到的大多数问题&#xff0c;在现实世界早就有了解法&#xff0c;今天再分享一个。 视频来自安阳市最后的朋克&#xff0c;张教练的实拍&#xff0c;视频中展示的是血糕&#xff0c;安阳市特产&#xff0c;不了解的可以将其等同于 “一种必须…

【JavaScript】JavaScript 变量 ① ( JavaScript 变量概念 | 变量声明 | 变量类型 | 变量初始化 | ES6 简介 )

文章目录 一、JavaScript 变量1、变量概念2、变量声明3、ES6 简介4、变量类型5、变量初始化 二、JavaScript 变量示例1、代码示例2、展示效果 一、JavaScript 变量 1、变量概念 JavaScript 变量 是用于 存储数据 的 容器 , 通过 变量名称 , 可以 获取 / 修改 变量 中的数据 ; …

Util工具类功能设计与类设计(http模块一)

目录 类功能 类定义 类实现 编译测试 Split分割字符串测试 ReadFile读取测试 WriteFile写入测试 UrlEncode编码测试 UrlDecode编码测试 StatuDesc状态码信息获取测试 ExtMime后缀名获取文件mime测试 IsDirectory&IsRegular测试 VaildPath请求路径有效性判断测…

Day33-计算机基础3

Day33-计算机基础3 1.根据TCP/IP进行Linux内核参数优化1.1 例1&#xff1a;调整访问服务端的【客户端】的动态端口范围 &#xff0c;LVS&#xff08;10-50万并发&#xff09;&#xff0c;NGINX负载&#xff0c;SQUID缓存服务,1.2 企业案例&#xff1a;DOS攻击的案例&#xff1a…

工资低适合下班做的6大副业,每一个都值得尝试!

2024年是最适合发展个人副业的时候&#xff01;无论你是否有全职工作&#xff0c;如果你的主业还不能满足你的成就感&#xff0c;还不能满足你的生活需求&#xff0c;这6个下班可以做的副业都很值得尝试&#xff01; 千金宝库做简单的网络任务 近年来&#xff0c;随着互联网技…

算法详解——leetcode150(逆波兰表达式)

欢迎来看博主的算法讲解 博主ID&#xff1a;代码小豪 文章目录 逆波兰表达式逆波兰表达式的作用代码将中缀表达式转换成后缀表达式文末代码 逆波兰表达式 先来看看leetcode当中的原题 大多数人初见逆波兰表达式的时候大都一脸懵逼&#xff0c;因为与平时常见的表达式不同&am…

C语言学习笔记,学懂C语言,看这篇就够了!(中)

附上视频链接&#xff1a;X站的C语言教程 目录 第8章、函数8.1 函数是什么8.2 函数的分类8.2.1 库函数8.2.1.1 如何使用库函数 8.2.2 自定义函数 8.3 函数参数8.3.1 实际参数(实参)8.3.2 形式参数(形参) 8.4 函数调用8.4.1 传值调用8.4.2 传址调用8.4.3 练习 8.5 函数的嵌套调…