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

文章目录

  • 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.mzph.cn/news/193297.shtml

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

相关文章

Git 标签管理

前言 标签 tag&#xff0c;就相当于对 某一次的 commit 做一个标识&#xff0c;起了一个别名&#xff0c;例如&#xff1a;在某个项目发布版本的时候&#xff0c;可针对最后一次 commit 起一个别名 v1.0 来标识这一次的commit。tag 的作用&#xff1a;commit id 相对于 tag 是很…

机械专业个人简历17篇

以下简历内容以机械专业相关岗位招聘需求为背景&#xff0c;我们整理了17篇且具有参考价值的简历案例&#xff0c;大家可以灵活借鉴&#xff0c;助理大家在众多候选人中脱颖而出。 机械专业简历模板下载&#xff08;可在线编辑制作&#xff09;&#xff1a;来幻主简历&#xf…

GEE:均值滤波

作者:CSDN @ _养乐多_ 本文将介绍在 Google Earth Engine(GEE)平台上,进行均值滤波操作的代码框架、核心函数和多种卷积核。 并分别以林地区域和农田区域为试验区,以NDVI图像为例。结果如下图所示, 文章目录 一、均值滤波二、完整代码三、代码链接一、均值滤波 均值滤…

CTF-PWN-堆-【malloc和free的工作流程】

文章目录 关于ptmalloc的思考缓存思想 chunk结构large bin补充fast bin 补充unsorted bin 补充top chunk 补充mmaped chunk补充Last remainder补充last remainder的产生 malloc_state补充mmap收缩阈值mmap分配阈值ptmalloc响应用户内存分配要求工作流程free时工作流程 大佬的关…

【Delphi】实现彩色日志显示框

目录 一、前言 二、实现方法 1. 第一步 2. 第二步 3. 第三步 三、主程序代码 四、下载 1. 可执行程序 2. 程序源代码 一、前言 在用Delphi做日常开发的时候&#xff0c;经常需要显示程序运行的日志&#xff0c;一般我们会使用TMemo&#xff0c;使用起来简单&#xff0c…

ElementPlus中 使用ElLoading.service, spinner: ‘el-icon-loading‘不生效

let downloadLoadingInstance ElLoading.service({ text: "正在下载数据&#xff0c;请稍候",spinner: el-icon-loading, background: "rgba(0, 0, 0, 0.7)", })使用以上代码时&#xff0c;加载的圆圈出不来&#xff0c;使用f12查看&#xff0c;即使能出…

BEVFormer环境配置

官网的教程说是Step By Step&#xff0c;但是实际上我按照步骤安装下来运行不了&#xff08;BEVFormer GitHub地址&#xff09;。主要是安装后关于包依赖产生的某些错误&#xff0c;特别是安装nuscenes-devkit没有在步骤中列出来&#xff0c;后面就不好解决某些包的版本依赖了。…

多级缓存自用

1.什么是多级缓存 传统的缓存策略一般是请求到达Tomcat后,先查询Redis,如果未命中则查询数据库,如图: 存在下面的问题: •请求要经过Tomcat处理,Tomcat的性能成为整个系统的瓶颈 •Redis缓存失效时,会对数据库产生冲击 多级缓存就是充分利用请求处理的每个环节,添加缓…

C语言实现猜数字游戏

前面我们已经了解了分支循环、数据类型及变量的知识点&#xff0c;今天我将用之前学过的知识进行实操&#xff0c;将所学的知识进行巩固和提升。下面的讲解仅我个人认知水平&#xff0c;如有欠缺之处&#xff0c;欢迎大家指正&#xff0c;并且我希望初学者在看完讲解后可以独立…

强化学习------时序差分(Temporal-Difference Learning)

简介 时序差分方法&#xff08;Temporal-Difference Learning&#xff09;简称TD算法是强化学习中非常经典的一种方法&#xff0c;Sarsa算法和Q-learning算法都是基于时序差分这种方法的。 强化学习分为基于模型和不基于模型的方法 基于模型的方法&#xff1a;是一种通过建立…

Redis之五大基础数据类型(详细总结 面试必备)

Redis之五大基础数据类型 Redis 共有 5 种基本数据类型&#xff1a;String&#xff08;字符串&#xff09;、List&#xff08;列表&#xff09;、Set&#xff08;集合&#xff09;、Hash&#xff08;散列&#xff09;、Zset&#xff08;有序集合&#xff09;。 这 5 种数据类…

64. 最小路径和(Leetcode)

文章目录 前言一、题目分析二、算法原理1.状态表示2.状态转移方程3.初始化4.填表顺序5.返回值是什么 三、代码实现总结 前言 在本文章中&#xff0c;我们将要详细介绍一下Leetcode6最小路径相关的内容 一、题目分析 二、算法原理 1.状态表示 列出dp表&#xff0c;dp[i][j]代…

IDEA导入JavaWeb项目(Maven)

IDEA导入JavaWeb(Maven)项目教程 运行教程 亲爱的粉丝们&#xff0c;我深知你们对IDEA导入JAVAWeb工程的迫切需求。在这个充满竞争的时代&#xff0c;每一个项目都离不开高效的沟通。过程中需要对应的环境适配和软件安…

操作PDF相关的工具,EPUB转PDF,golang

unipdf 安装依赖 go get github.com/unidoc/unipdf/v3 示例代码 https://github.com/unidoc/unipdf-examples 获取KEY 登录 https://cloud.unidoc.io/ 注册账号&#xff0c;生成 KEY&#xff0c;但是需要收费。 chromedp 使用Golang编写&#xff0c;主要功能是调用浏览器内…

代码随想录算法训练营第四十一天 _ 动态规划_343. 整数拆分、96.不同的二叉搜索树、01背包问题。

学习目标&#xff1a; 动态规划五部曲&#xff1a; ① 确定dp[i]的含义 ② 求递推公式 ③ dp数组如何初始化 ④ 确定遍历顺序 ⑤ 打印递归数组 ---- 调试 引用自代码随想录&#xff01; 60天训练营打卡计划&#xff01; 学习内容&#xff1a; 343. 整数拆分 动态规划五步曲&…

Mysql之数据处理增删改

Mysql之数据处理增删改查 插入数据INSERT INTO语句的使用INSERT 与子查询结合 更新数据(修改数据)UPDATE SET语句 删除数据DELETE FROM语句 Mysql8新特性&#xff1a;计算列 插入数据 INSERT INTO语句的使用 用 INSERT INTO 语句&#xff0c;向表中插入数据 方式一&#xff1a;…

Web漏洞分析-SQL注入XXE注入(上)

随着互联网的不断普及和Web应用的广泛应用&#xff0c;网络安全问题愈发引起广泛关注。在网络安全领域中&#xff0c;SQL注入和XXE注入是两个备受关注的话题&#xff0c;也是导致许多安全漏洞的主要原因之一。本博客将深入研究这两种常见的Web漏洞&#xff0c;带您探寻背后的原…

一个用c#瞎写的sftp工具

0.下载地址 https://wwus.lanzouj.com/iOZUv1gkgpze 密码:123456 1.能进行单个和批量下载, 没有弄上传 2.速度奇差,可能是某些地方没弄好.有一定的进度显示,但是不太准. 3.很多地方没弄好,有能力的自己弄一下 4.在app.config文件配置sftp

深度学习 第3章 Python程序设计语言(3.2 Python程序流程控制)

无论是在机器学习还是深度学习中&#xff0c;Python已经成为主导性的编程语言。而且&#xff0c;现在许多主流的深度学习框架&#xff0c;例如PyTorch、TensorFlow也都是基于Python。本课程主要是围绕“理论实战”同时进行&#xff0c;所以本章将重点介绍深度学习中Python的必备…

Echarts大屏可视化_03 定制柱状图

柱状图模块引入 1.找到合适的图表 在echarts中寻找与目标样式相近的图表 Examples - Apache ECharts 2. 引入柱状图 使用立即执行函数构建&#xff0c;防止变量全局污染 实例化对象 将官网中提供的option复制到代码中&#xff0c;并且构建图表 // 柱状图模块1 (function () {/…