C++:C++11新特性--lambda表达式和包装器

news2025/1/20 5:57:00

文章目录

  • lambda表达式
    • lambda表达式的使用规则
    • lambda表达式的用法
    • lambda表达式的理解
    • 函数对象和lambda表达式
  • 包装器
  • bind

lambda表达式

首先介绍什么是lambda表达式,在介绍这个情景前,可以回忆一下算法库中的sort排序:

// lambda表达式
void lambda1()
{
	int arr[] = { 1,3,6,5,4,2,8,9,10 };
	for (auto e : arr)
		cout << e << " ";
	cout << endl;
	sort(arr, arr + sizeof(arr) / sizeof(arr[0]));
	for (auto e : arr)
		cout << e << " ";
	cout << endl;
}

这是可以实现排序的结果的,但排序的默认是使用升序排序,这是因为在算法库中最后一个参数给了缺省参数

在这里插入图片描述
因此,如果想要实现降序排序,可以自己定义一种排序的方式

// lambda表达式
struct Compare
{
	bool operator()(int a, int b)
	{
		return a > b;
	}
};

void lambda1()
{
	int arr[] = { 1,3,6,5,4,2,8,9,10 };
	for (auto e : arr)
		cout << e << " ";
	cout << endl;
	sort(arr, arr + sizeof(arr) / sizeof(arr[0]), Compare());
	for (auto e : arr)
		cout << e << " ";
	cout << endl;
}

此时就可以实现一个降序排序

这样的实现是很有意义的,当遇到不能进行默认比较的时候,例如在比较pair类型参数等,就不可以直接进行比较,需要手动的定义比较的方式,这都是可以理解的

现在的问题是,这样写有一个不方便的地方,就是传入的Compare参数并不知道是按照什么规则进行排序的,是升序还是降序?这是不确定的

因此C++11就引入了lambda表达式来弥补这方面的措施,lambda表达式最早出现于Python语言,因此从某种意义来说可以把它当成是一种全新的语言来学习它,那么下面就介绍它的使用规则

lambda表达式的使用规则

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

对于上面表达式中的各部分写一个说明:

  1. [capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。

  2. (parameters):参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略

  3. mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。->returntype:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。

  4. {statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。

注意点:

lambda函数定义的过程中,参数列表和返回值的类型都是可以选择的,而捕捉列表和函数体也可以为空,那么空语句可以定义为[]{}表示这个lambda函数不做任何事

lambda表达式的用法

void lambda2()
{
	int arr[] = { 1,3,6,5,4,2,8,9,10 };
	for (auto e : arr)
		cout << e << " ";
	cout << endl;
	// lambda表达式的完全版写法
	sort(arr, arr + sizeof(arr) / sizeof(arr[0]), [](int x, int y) -> bool {return x < y; });
	// 缺省方式的写法
	sort(arr, arr + sizeof(arr) / sizeof(arr[0]), [](int x, int y) {return x < y; });
	for (auto e : arr)
		cout << e << " ";
	cout << endl;
}

上面演示的就是lambda表达式的一个例子,可以看到的是,整个函数的组成是由捕获列表,参数列表,mutable选项(这里没写),返回值类型,函数体所组成的,而事实上这也确实就是lambda表达式的组成,从这个表达式中看就能很明白的看出lambda表达式的功能是什么了

lambda表达式还可以用类似于函数的方式来完成:

void lambda3()
{
	auto fun1 = [](int x, int y)
	{
		cout << x + y << endl;
	};
	auto fun2 = [](int x, int y)
	{
		return x + y;
	};

	fun1(10, 20);
	cout << fun2(1, 2) << endl;
}

上面的表达式可以看出,fun1fun2接收了这个表达式,接着就可以用函数调用的方式来对lambda表达式进行执行,而事实上也确实成功的执行了,是有运行结果的

捕捉列表

捕捉列表是lambda表达式的一个重要组成部分,它表示了lambda表达式中可以使用哪些数据,以及使用的是传值还是传引用

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

lambda表达式的理解

lambda表达式从上面来看是有返回值的,那么返回值的类型是什么呢?

运行结果如下所示

void lambda4()
{
	auto fun1 = [](int x, int y)
	{
		cout << x + y << endl;
		return 0;
	};
	fun1(10, 20);
	cout << typeid(fun1).name() << endl;
}
30
class `void __cdecl lambda4(void)'::`2'::<lambda_1>

从中看出,lambda表达式的类型很奇怪,并不是想象中的是一种固定的模式,而是一种类似于随机值的机制,由此说明,lambda表达式之间是不可以进行相互赋值的,因为它们在底层是完完全全不一样的,不支持operator=

函数对象和lambda表达式

函数对象是什么?lambda表达式是什么?

函数对象又被叫做仿函数,如同它字面意思一样,可以像函数一样使用对象,简单来说就是前面在sort中写的Compare对象,在它里面重载了一个operator(),这样近似的可以理解成是把一个对象当成一个函数来使用

来举一个例子:

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

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

private:
	double _rate;
};

void lambda6()
{
	double rate = 1;
	Rate r1(rate);
	r1(1, 2);
	auto r2 = [=](double monty, int year)->double {return monty * rate * year;
	};
	r2(1, 2);
}

从使用的角度来看,函数对象和lambda表达式是一样的,都是用括号进行调用,函数对象将rate作为它的成员变量,在定义对象的时候给出初始值,而lambda表达式又可以通过捕获列表将该变量直接捕获到

而从它们的底层逻辑考虑,在底层看来表达式的处理方式是一样的,都是按照函数对象来处理的,也就是说,如果定义了一个lambda表达式,在系统的底层会自动生成一个类,在这个类中会重载一个operator()

包装器

什么是包装器?

function包装器,也叫做适配器,是C++中的一个类模板

为什么需要包装器?

回忆一下在学习C和C++的过程中,对于可调用对象的概念来说,可以如何进行调用?可以调用什么呢?

  1. 函数指针
  2. 仿函数
  3. lambda表达式

借助这三个内容,都可以把对象近似当成一个函数来调用,但是这是有弊端的,例如对于函数指针来说,它的使用非常的复杂,不方便使用,对于阅读者来说也很难进行阅读,对于仿函数来说它自身的包装很重,需要构造一个结构体,在里面实现一个函数的重载,而对于lambda表达式来说就更有弊端了,它本身是一个匿名的内容,想要实现调用也并不容易,因此这三种调用的方式都有一定的弊端,都不太容易进行调用

那么对此可以如何进行针对性的解决呢?C++11就引入了一个function包装器,简单来说就是把这些内容进行了一定程度的包装,在调用的时候直接调用

包装器的使用方法

// 包装器
// 定义几种方式用来实现两个数的相加过程
int func1(int x, int y)
{
	return x + y;
}

struct func2
{
	int operator()(int x, int y)
	{
		return x + y;
	}
};

class func3
{
public:
	static int add1(int x, int y)
	{
		return x + y;
	}

	double add2(double x, double y)
	{
		return x + y;
	}
};

void function1()
{
	// 把函数指针包装起来
	function<int(int, int)> fun1 = func1;
	cout << "函数指针" << fun1(10, 20) << endl;

	// 把仿函数包装起来
	function<int(int, int)> fun2 = func2();
	cout << "仿函数" << fun2(20, 30) << endl;

	// 把lambda表达式包装起来
	function<int(int, int)> fun3 = [](int x, int y) {return x + y; };
	cout << "lambda表达式" << fun3(40, 50) << endl;

	// 把类的成员函数包装起来
	function<int(int, int)> fun4 = &func3::add1;
	cout << "静态成员函数" << fun4(50, 60) << endl;
	function<double(func3, double, double)> fun5 = &func3::add2;
	cout << "非静态成员函数" << fun5(func3(), 1.1, 2.2) << endl;
}

上面演示的就是包装器的使用方法,它的好处之一就是,可以把多种调用的方式变成一种来调用,除了非静态成员函数,这个后面和绑定器结合在一起进行讲解

包装器的底层逻辑可以使得模板实例化的成本降低,在实际的开发中还是有意义的

bind

std::bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。一般而言,我们用它可以把一个原本接收N个参数的函数fn,通过绑定一些参数,返回一个接收M个(M可以大于N,但这么做没什么意义)参数的新函数。同时,使用std::bind函数还可以实现参数顺序调整等操作

bind的使用

关于bind的使用,可以大致有绑定成员函数,参数调换顺序两种使用场景

所以上面的非静态成员变量实际上也是可以进行改造的,可以改造成这样

绑定成员函数

	function<double(double, double)> fun5 = bind(&func3::add2, func3(), placeholders::_1, placeholders::_2);
	cout << "非静态成员函数" << fun5(1.1, 2.2) << endl;

这个函数相当于是把fun5死绑在func3的对象中了

参数调换顺序

// bind
int sub(int x, int y)
{
	return x - y;
}
void testbind()
{
	function<int(int, int)> fun1 = sub;
	cout << fun1(10, 5) << endl;

	function<int(int, int)> fun2 = bind(sub, placeholders::_2, placeholders::_1);
	cout << fun2(10, 5) << endl;
}

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

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

相关文章

HADOOP::Fsimage和Edits解析

NameNode被格式化之后&#xff0c;将在/opt/module hadoop-3.1.3/data/tmp/dfs/name/curent目录 中产生如下文件 fsimage_ 0000000000000000000 fsimage_ 0000000000000000000.md5 seen_txid VERSION (1) Fsimage文件: HDFS文件系统元数据的一个永久性的检查点&#xff0…

基于微信小程序的高校活动系统

1 前言 1.1开发背景及意义 高校课余活动管理是中职学生素质教育的重要途径及有效方式&#xff0c;特别是对于一个院校的校园文化建设、校风学风建设和学生综合素质方面的提高至关重要t叫"。良好的学生活动组织可以更好地调动学生参与活动&#xff0c;让学生展示自己的能力…

【超全】React学习笔记 中:进阶语法与原理机制

React学习笔记 React系列笔记学习 上篇笔记地址&#xff1a;【超全】React学习笔记 上&#xff1a;基础使用与脚手架 下篇笔记地址&#xff1a;【超全】React学习笔记 下&#xff1a;路由与Redux状态管理 React进阶组件概念与使用 1. React 组件进阶导读 在掌握了 React 的基…

Windows修改MAC地址的方法(以windows11为例)

我们在日常的工作中&#xff0c;如果mac地址被限制&#xff0c;就需要修改mac地址&#xff0c;本文总结一下修改windows的mac地址的方法。 方法一&#xff1a;网络适配器中配置 网络适配器中配置的方式适用于能够在网络适配器中找到物理地址(NetworkAddress)的情况。 1、打开…

CPU标高load标高;linux故障日志排查

一般情况下&#xff0c;服务器不太会出问题。但是遇到特别诡异的情况&#xff0c;多半是服务器本身的问题。遇到问题&#xff0c;我们不能一味的去排查应用&#xff0c;中间件。更应该想到服务器的问题。否则很容易出现南辕北辙的情况。 这次分享的是一次服务器故障&#xff0c…

JavaScript添加快捷键、取消浏览器默认的快捷操作、js查看键盘按钮keycode值

document.addEventListener("keydown",function (event) {// 如果不知道按键对应的数字&#xff08;keyCode&#xff09;是多少可以弹出查看一下// alert(event.keyCode)if (event.ctrlKey && event.altKey && event.view["0"] null){if(…

Spring学习笔记:Day2

昨天定的学习计划发现通过文心4.0来实现不靠谱&#xff0c;坑太多&#xff0c;今天开始跟随B站进行学习&#xff0c;争取10-15天学习一遍&#xff0c;冲啊&#xff01; 地址&#xff1a;001-课程介绍_哔哩哔哩_bilibili 今日规划&#xff1a; pt 001 - pt 018&#xff0c;提到…

Vue3实现滚动到容器底部时发送请求,加载新数据

问题来源 在项目中出现了需要在容器滚动到底部时&#xff0c;加载新的数据的需求&#xff0c;以下是解决的方案笔记 解决 画了个流程图&#xff1a; 如图&#xff0c;先添加一个动态加载的图标&#xff0c;还有全部数据载完的《到底啦...》 大概这么个样子&#xff0c;之后呢…

【C++ Primer Plus学习记录】第5章复习题

1.入口条件循环和出口条件循环之间的区别是什么&#xff1f;各种C循环分别属于其中的哪一种&#xff1f; C提供了3种循环&#xff1a;for、while和do while循环。for和while循环都是入口条件循环&#xff0c;意味着程序将在执行循环体中的语句之前检查测试条件。do while循环是…

【IDEA】导入项目发现目录消失

错误截图 这个目录里我是有文件的&#xff0c;但是导入到idea后却看不见文件 错误原因&#xff1a; 删除目录的时候不小心将.iml文件给删除了&#xff01; 解决办法 进入项目结构 选择模块&#xff0c;点击 导入模块 选择需要导入的模块的目录 从现有项目创建模块 --> 下…

智慧用电安全动态监控系统

智慧用电安全动态监控系统是一种先进的电力监控技术系统&#xff0c;它运用物联网、大数据、云计算等先进技术&#xff0c;对电力系统的运行状况进行实时监控和预警。 该系统依托电易云-智慧电力物联网&#xff0c;通过智能传感终端采集电气线路的实时运行数据&#xff0c;客户…

Redis的高可用模式

1. 什么是高可用&#xff1f; 高可用&#xff08;High Availability, HA&#xff09;是指在信息技术中确保系统、服务或应用程序在绝大多数时间内都是可操作和可访问的能力。这通常涉及以下几个关键方面&#xff1a; 最小化停机时间: 高可用系统的目标是减少因硬件故障、系统升…

【面试HOT200】二叉树——广度优先搜索篇

系列综述&#xff1a; &#x1f49e;目的&#xff1a;本系列是个人整理为了秋招面试的&#xff0c;整理期间苛求每个知识点&#xff0c;平衡理解简易度与深入程度。 &#x1f970;来源&#xff1a;材料主要源于【CodeTopHot200】进行的&#xff0c;每个知识点的修正和深入主要参…

怎样使用rtsp,rtmp摄像头低延时参于Web视频会议互动直播

业务系统中有大量的rtsp&#xff0c;rtmp等监控直播设备&#xff0c;原大部分都是单一业务监控直播之类&#xff0c;目前很多业务需要会议互动&#xff0c;需要监控参会&#xff0c;提出需摄像头拉流参会的需求&#xff0c;由于rtmp&#xff0c;rtsp原生不支持web播放&#xff…

springboot足球社区管理系统

springboot足球社区管理系统 成品项目已经更新&#xff01;同学们可以打开链接查看&#xff01;需要定做的及时联系我&#xff01;专业团队定做&#xff01;全程包售后&#xff01; 2000套项目视频链接&#xff1a;https://pan.baidu.com/s/1N4L3zMQ9nNm8nvEVfIR2pg?pwdekj…

activemq启动成功但web管理页面却无法访问

前提&#xff1a; 在linux启动activemq成功&#xff01;本地能ping通linux 处理方案&#xff1a; 确定防火墙是否关闭&#xff0c; 有两种处理方案&#xff1a;第一种-关闭防火墙&#xff1b;第二种-暴漏8161和61616两个端口 netstat -lnpt查看8161和61616端口 注意&#xf…

【超全】React学习笔记 下:路由与Redux状态管理

React学习笔记 React系列笔记学习 上篇笔记地址&#xff1a;【超全】React学习笔记 上&#xff1a;基础使用与脚手架 中篇笔记地址&#xff1a;【超全】React学习笔记 中&#xff1a;进阶语法与原理机制 React路由概念与理解使用 1. 引入 React路由是构建单页面应用(SPA, Sin…

【斗破年番】小医仙沐浴以毒攻毒,彩鳞怒杀蝎毕岩,泪目暂时下线

Hello,小伙伴们&#xff0c;我是拾荒君。 《斗破苍穹年番》国漫更新啦&#xff01;刚更新&#xff0c;拾荒君就和我的小伙伴们去观看了。在斗破年番第73集中&#xff0c;蝎毕岩为了击败萧炎&#xff0c;不惜耗尽全身实力释放出魔毒斑&#xff0c;假装攻击小医仙&#xff0c;却…

C++ 基础篇

目录 C开发概述 C特点 C跨平台的原因 C编译器 C库 操作系统API C基本概念 注释 变量 常量 两种定义常量方式的区别 表示符命名规则 常见的关键字 数据类型 整型 浮点数 字符型 转义字符 字符串型 布尔类型 运算符 算术运算符 赋值运算符 比较运算符 逻…

linux常用命令-find命令与scp命令详解(超详细)

文章目录 前言一、find命令介绍1. find命令简介2. find命令的基本语法3. 常用的find命令选项和表达式 二、find命令示例用法1. 按照名称进行搜索2. 按照类型进行搜索3. 按照修改时间进行搜索4. 按照文件大小进行搜索5. 对搜索到的文件执行指定的命令6. 删除搜索到的文件 三、sc…