【C++杂货铺】C++11新特性——lambda

在这里插入图片描述

文章目录

  • 一、C++98中的排序
  • 二、先来看看 lambda 表达式长什么样
  • 三、lambda表达式语法
    • 3.1 捕捉列表的使用细节
  • 四、lambda 的底层原理
  • 五、结语

一、C++98中的排序

在 C++98 中,如果要对一个数据集合中的元素进行排序,可以使用 std::sort 方法,下面代码是对一个整型集合进行排序。

#include <algorithm>
#include <functional>
#include <iostream>
using namespace std;
int main()
{int array[] = { 4,1,8,5,3,7,0,9,2,6 }; cout << "原始数组:";for (auto e : array){cout << e << ' ';}cout << endl << endl << "排升序:";// 默认按照小于比较,排出来结果是升序std::sort(array, array + sizeof(array) / sizeof(array[0]));for (auto e : array){cout << e << ' ';}cout << endl << endl << "排降序:";// 如果需要降序,需要改变元素的比较规则std::sort(array, array + sizeof(array) / sizeof(array[0]), greater<int>());for (auto e : array){cout << e << ' ';}return 0;
}

在这里插入图片描述
小Tips:上面的 greater 是一个仿函数,这里传递仿函数是用来控制大小比较的。关于仿函数,在前面的文章中已经多次使用,在【C++杂货铺】优先级队列的使用指南与模拟实现一文中,我们使用仿函数来进行大小比较;在【C++杂货铺】一文带你走进哈希:哈希冲突 | 哈希函数 | 闭散列 | 开散列一文中,我们使用仿函数来获取 pair<K, V> 模型中的 key。总之,仿函数有着十分强大的功能。

如果待排序的元素为自定义类型,由于自定义类型中可能有多重不同的属性,因此需要用户自己来定义排序时的比较规则:

struct Goods
{string _name;  // 名字double _price; // 价格int _evaluate; // 评价Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){}
};ostream& operator<<(ostream& out, Goods& goods)
{out << goods._name << '-' << goods._price << '-' << goods._evaluate;return out;
}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;}
};struct CompareevaluateLess
{bool operator()(const Goods& gl, const Goods& gr){return gl._evaluate < gr._evaluate;}
};
struct CompareevaluateGreater
{bool operator()(const Goods& gl, const Goods& gr){return gl._evaluate > gr._evaluate;}
};
int main()
{vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };cout << "排序前:";for (auto e : v){cout << e << ' ';}cout << endl << endl << "按照价格排升序:";sort(v.begin(), v.end(), ComparePriceLess());for (auto e : v){cout << e << ' ';}cout << endl << endl << "按照价格排降序:";sort(v.begin(), v.end(), ComparePriceGreater());for (auto e : v){cout << e << ' ';}cout << endl << endl << "按照评价排升序:";sort(v.begin(), v.end(), CompareevaluateLess());for (auto e : v){cout << e << ' ';}cout << endl << endl << "按照评价排降序:";sort(v.begin(), v.end(), CompareevaluateGreater());for (auto e : v){cout << e << ' ';}cout << endl;
}

在这里插入图片描述

小Tips:上面代码中如果要使用库里面的仿函数 lessgreater,需要对 >< 进行运算符重载,实现 Goods 类的大小比较。但是上面的代码中并没有采取这种做法,而是直接自己写了两个仿函数进行大小关系的比较。前面那种提供运算符重载的方法比较局限,因为无论是 < 还是 > 都只能重载一份,即 operator<operator> 各自只能在代码中出现一份,且它们的内部只能根据一种属性进行大小比较,在同一段代码中不能实现根据不同属性去排序。而自己写仿函数就不会出现这种情况,我们可以在同一段代码中根据不同的属性去写不同的仿函数(只要类名不同即可)。

随着 C++ 语法的发展,人们开始觉得上面的写法太复杂了,每次为了实现一个排序算法,都要重新去写一个类(仿函数,实现大小比较),如果每次比较的逻辑不一样,还要去实现多个类,特别是相同类的命名,这些都给编程者带来了极大的不便。因此,在 C++11 语法中出现了 Lambda 表达式。

二、先来看看 lambda 表达式长什么样

struct Goods
{string _name;  // 名字double _price; // 价格int _evaluate; // 评价Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){}
};int main()
{vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };cout << "排序前:";for (auto e : v){cout << e << ' ';}cout << endl << endl << "按照价格排升序:";sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool {return g1._price < g2._price; });for (auto e : v){cout << e << ' ';}cout << endl << endl << "按照价格排降序:";sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool {return g1._price > g2._price; });for (auto e : v){cout << e << ' ';}cout << endl << endl << "按照评价排升序:";sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool {return g1._evaluate < g2._evaluate; });for (auto e : v){cout << e << ' ';}cout << endl << endl << "按照评价排降序:";sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool {return g1._evaluate > g2._evaluate; });for (auto e : v){cout << e << ' ';}cout << endl;
}

在这里插入图片描述
小Tips:上述代码就是使用 C++11 中的 lambda 表达式来解决,可以看出 lambda 表达式实际是一个匿名函数对象

三、lambda表达式语法

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

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

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

  • mutable:默认情况下,lambda 函数总是一个 const 函数,mutable 可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。

  • ->returntype:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可以省略。返回值类型明确的情况下,也可以省略,由编译器对返回值类型进行推断。

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

小Tips:在 lambda 函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为空。因此 C++11 中最简单的 lambda 函数为:[]{};。该 lambda 函数不能做任何事情。

实例

int AddFunc(int x, int y)
{return x + y;
}int num1 = 10, num2 = 20;int main()
{// 实现两个数相加的 lambda 函数int a = 1, b = 10;auto add = [](int x, int y)->int {return x + y; };cout << add(a, b) << endl;// 实现两个函数交换的 lambda 函数auto swap = [add, a, b](int& x, int& y){int tmp = x;x = y;y = tmp;// cout << add(a, b) << endl; // 在 lambda 函数的函数体中无法直接使用局部的 lambda 函数或者变量(对象).//cout << AddFunc(a, b) << endl; // 在 lambda 函数的函数体中可以直接使用全局的函数或者变量(对象).cout << AddFunc(num1, num2) << endl; // 在 lambda 函数的函数体中可以直接使用全局的函数或者变量(对象).};swap(a, b);return 0;
}

小Tips:在 lambda 函数的函数体中可以调用全局的函数,使用全局的变量。但是要想在 lambda 的函数体中使用局部的变量或对象,则需要通过捕捉列表或者参数列表。

3.1 捕捉列表的使用细节

捕捉列表描述了上下文中哪些数据可以被 lambda 使用,以及使用的方式是传值还是传引用。

  • [var]:表示值传递方式捕捉变量 var。

  • [=]:表示值传递方式捕捉所有父作用域中的变量(包括this)。

  • [&var]:表示引用传递捕捉变量 var。

  • [&]:表示引用方式传递捕捉所有父作用域中的变量(包括this)。

  • [this]:表示值传递方式捕捉当前的 this 指针。

小Tips

  • 父作用域指包含 lambda 函数语句块的作用域。

  • 值传递方式捕捉到的变量本质上是父作用域中变量的一份拷贝,在默认情况下会对捕捉到的变量加上 const 属性,即不可以在 lambda 函数体中对捕捉到的变量进行修改。如果想修改可以加上 mutable 取消其常量性。即 [x, y] 捕捉列表中的 xy 是父作用域中 xy 的一份拷贝,并且默认给捕捉列表中的 xy 加上了 const 属性。

  • 引用传递方式既可以捕捉普通的变量也可以捕捉 const 变量。

  • 语法上捕捉列表可以由多个捕捉项组成,并以逗号隔开。例如:[=, &a, &b]:以引用传递的方式捕捉变量 ab,以值传递方式捕捉其他所有变量;[&, a, this]:以值传递方式捕捉变量 athis,以引用方式捕捉其他变量。如果由多个捕捉项组成,=& 只能出现在捕捉列表的开头,即 [&a, &b, = ] 这样写是错误的。

  • 捕捉列表不允许变量重复传递,否则就会导致编译出错。例如:[=, a]:= 已经以值传递的方式捕捉了所有变量,在去捕捉 a 就会导致重复捕捉。

  • 在块作用域以外的 lambda 函数捕捉列表必须为空。

  • 在块作用域中的 lambda 函数仅能捕捉父作用域中的局部变量,捕捉任何非此作用域或者 非局部变量都会导致编译报错。

  • lambda 表达式之间不能相互赋值,即使看起来类型相同。

四、lambda 的底层原理

lambda 看起来很厉害,但它本质上就是仿函数。

int main() 
{auto func1 = [](int x, int y) {return x + y; };auto func2 = [](int x, int y) {return x + y; };cout << typeid(func1).name() << endl;cout << typeid(func2).name() << endl;func1(1, 2);return 0;
}

在这里插入图片描述

如上面代码所示,两个仿函数对象 func1func2 它们看起来是一模一样的,但是通过打印它们各自的类型可以看出,它们的类型有所不同,因此 func1func2 本质上就是两个不同的类对象,所以 lambda 表达式之间不能相互赋值,即使看起来类型相同。func1(1, 2) 本质上就是 func1 这个仿函数的对象在调用 operator()

在这里插入图片描述

五、结语

今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下,春人的主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是春人前进的动力!

在这里插入图片描述

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

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

相关文章

二叉树顺序结构与堆的概念及性质(c语言实现堆)

上次介绍了树&#xff0c;二叉树的基本概念结构及性质&#xff1a;二叉树数据结构&#xff1a;深入了解二叉树的概念、特性与结构 今天带来的是&#xff1a;二叉树顺序结构与堆的概念及性质&#xff0c;还会用c语言来实现堆 文章目录 1. 二叉树的顺序结构2.堆的概念和结构3.堆…

推荐几个开源HTTP服务接口快速生成工具

在现在流行微服务、前后端分离软件开发架构下&#xff0c;基于标准RESTful/JSON的HTTP接口已经成为主流。在实际业务中有很多需要快速开发调用数据服务接口的需求&#xff0c;但团队中缺乏专业的后端开发人员&#xff0c;比如&#xff1a; &#xff08;1&#xff09;数据库表已…

PHP开发日志 ━━ 基于PHP和JS的AES相互加密解密方法详解(CryptoJS) 适合CryptoJS4.0和PHP8.0

最近客户在做安全等保&#xff0c;需要后台登录密码采用加密方式&#xff0c;原来用个base64变形一下就算了&#xff0c;现在不行&#xff0c;一定要加密加key加盐~~ 前端使用Cypto-JS加密&#xff0c;传输给后端使用PHP解密&#xff0c;当然&#xff0c;前端虽然有key有盐&…

如何学习计算机编程?零基础入门,轻松成为编程达人!

在这个信息爆炸的时代&#xff0c;计算机编程已经成为一项炙手可热的技能。如果你也对编程充满兴趣&#xff0c;但又不知从何入手&#xff0c;那么本文将为你提供一条通往编程世界的捷径。掌握了这些技巧&#xff0c;相信你一定能够轻松成为编程达人&#xff01; 一、选择合适…

lag-llama源码解读(Lag-Llama: Towards Foundation Models for Time Series Forecasting)

Lag-Llama: Towards Foundation Models for Time Series Forecasting 文章内容&#xff1a; 时间序列预测任务&#xff0c;单变量预测单变量&#xff0c;基于Llama大模型&#xff0c;在zero-shot场景下模型表现优异。创新点&#xff0c;引入滞后特征作为协变量来进行预测。 获得…

爬虫工作量由小到大的思维转变---<第三十五章 Scrapy 的scrapyd+Gerapy 部署爬虫项目>

前言: 项目框架没有问题大家布好了的话,接着我们就开始部署scrapy项目(没搭好架子的话,看我上文爬虫工作量由小到大的思维转变---&#xff1c;第三十四章 Scrapy 的部署scrapydGerapy&#xff1e;-CSDN博客) 正文: 1.创建主机: 首先gerapy的架子,就相当于部署服务器上的;所以…

Ubuntu 18.04搭建RISCV和QEMU环境

前言 因为公司项目代码需要在RISCV环境下测试&#xff0c;因为没有硬件实体&#xff0c;所以在Ubuntu 18.04上搭建了riscv-gnu-toolchain QEMU模拟器环境。 安装riscv-gnu-toolchain riscv-gnu-toolchain可以从GitHub上下载源码编译&#xff0c;地址为&#xff1a;https://…

大华主动注册协议介绍

一、大华主动注册协议介绍 前面写了一篇文章&#xff0c;介绍一些设备通过大华主动注册协议接入到AS-V1000的文章&#xff0c;很多问我关于大华主动注册协议的相关知识。 由于大华主动注册协议是一种私有协议&#xff0c;通常不对外公开详细的协议规范和技术细节。因此…

C++ Primer Plus----第十二章--类和动态内存分布

本章内容包括&#xff1a;对类成员使用动态内存分配&#xff1b;隐式和显式复制构造函数&#xff1b;隐式和显式重载赋值运算符&#xff1b;在构造函数中使用new所必须完成的工作&#xff1b;使用静态类成员&#xff1b;将定位new运算符用于对象&#xff1b;使用指向对象的指针…

ssm基于web的志愿者管理系统的设计与实现+vue论文

摘 要 使用旧方法对志愿者管理系统的信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的网络信息技术运用在志愿者管理系统的管理上面可以解决许多信息管理上面的难题&#xff0c;比如处理数据时间很长&#xff0c;数据存在错误不能及时纠正等问题。这次开发的志愿者…

main参数传递、反汇编、汇编混合编程

week03 一、main参数传递二、反汇编三、汇编混合编程 一、main参数传递 参考 http://www.cnblogs.com/rocedu/p/6766748.html#SECCLA 在Linux下完成“求命令行传入整数参数的和” 注意C中main: int main(int argc, char *argv[]), 字符串“12” 转为12&#xff0c;可以调用atoi…

两种汇编的实验

week04 一、汇编-1二、汇编-2 一、汇编-1 1 通过输入gcc -S -o main.s main.c -m32 将下面c程序”week0401学号.c“编译成汇编代码 int g(int x){ return x3; } int f(int x){ int i 学号后两位&#xff1b; return g(x)i; } int main(void){ return f(8)1; } 2. 删除汇编代码…

『番外篇六』SwiftUI 取得任意视图全局位置的三种方法

概览 在 SwiftUI 开发中,利用描述性代码我们可以很轻松的构建各种丰富多彩的视图。我们可以设置它们的大小、位置、颜色并应用不计其数的修改器。 但是,小伙伴们是否想过在 SwiftUI 中如何获取一个视图的全局位置坐标呢? 在本篇博文中,您将学到如下内容: 概览1. SwiftU…

守护 C 盘,Python 相关库设置

文章目录 前言Python 相关查看所有 Python 安装位置查看 Python 依赖位置查看 conda 配置查看 env 列表移除指定 env创建 env进入 env删除环境位置目录添加环境位置 (将位置置顶)查看 pip 缓存位置设置 pip 缓存位置 其他进入 Temp修改位置 Python技术资源分享1、Python所有方向…

(001)Unit 编译 UTF8JSON

文章目录 编译 Dll编译报错附录 编译 Dll 新建工程&#xff1a; 注意 UnityEngineDll 的选择&#xff01;2022 版本的太高了&#xff01;&#xff01;&#xff01; 下载包&#xff0c;导入unity : 3. 将 unf8json 的源码拷贝到新建的工程。 4. 编译发布版本&#xff1a; 编译…

竞赛保研 基于卷积神经网络的乳腺癌分类 深度学习 医学图像

文章目录 1 前言2 前言3 数据集3.1 良性样本3.2 病变样本 4 开发环境5 代码实现5.1 实现流程5.2 部分代码实现5.2.1 导入库5.2.2 图像加载5.2.3 标记5.2.4 分组5.2.5 构建模型训练 6 分析指标6.1 精度&#xff0c;召回率和F1度量6.2 混淆矩阵 7 结果和结论8 最后 1 前言 &…

mongoose中http server服务器解决“Access-Control-Allow-Origin mongoose”跨域问题

问题 使用mongoose做http服务器&#xff0c;自己构造的浏览器端jquery在访问server时&#xff0c;会遇到&#xff1a; Access to XMLHttpRequest at http://127.0.0.1:8000/ from origin null has been blocked by CORS policy: No Access-Control-Allow-Origin header is pr…

python+django大自然环境保护宣传网站62r9b

本课题使用Python语言进行开发。基于web,代码层面的操作主要在PyCharm中进行&#xff0c;将系统所使用到的表以及数据存储到MySQL数据库中 本系统由后台管理子系统&#xff0c;登录子系统&#xff0c;按登陆角色及权限划分为管理员:个人中心&#xff0c;用户管理&#xff0c;文…

遇到DDOS怎么办,盾真的可以抗攻击吗

网络在以难以想象的速度发展&#xff0c;黑客们针对网络漏洞发起的攻击也从未停止&#xff0c;但复杂的网络环境让网络安全的维护更为艰难&#xff0c;如果游戏公司没有做好防御措施&#xff0c;黑客发起攻击只是时间问题。在网络攻击愈加多元化的今天&#xff0c;游戏行业可以…

懒加载的el-tree中没有了子节点之后还是有前面icon箭头的展示,如何取消没有子节点之后的箭头显示

没有特别多的数据 <template><el-tree:props"props":load"loadNode"lazyshow-checkbox></el-tree></template><script>export default {data() {return {props: {label: name,children: zones,isLeaf:"leaf",//关…