lambda表达式介绍

前言

        lambda表达式是C++11标准才支持的,有了它以后在一些地方进行使用会方便很多,尤其在一些需要仿函数的地方,lambda表达式完全可以替代它的功能。代码的可读性也会提高。

目录

1.lambda表达式

2.lambda表达式语法

3.函数对象和lambda表达式


1.lambda表达式

        在C++98中如果想要对一个数据集合进行排序,可以使用std::sort的方法 

//仿函数
template<class T>
struct Greater 
{bool operator()(const T& nums1,const T& nums2){return nums1 > nums2;}
};
//函数
bool FunGreater(const int&nums1,const int&nums2)
{return nums1 > nums2;
}
void TestSort()
{int array[] = { 4,1,8,5,3,7,0,9,2,6 };std::sort(array, array + sizeof(array)/sizeof(array[0]) );//如果按照默认的方式进行排序那么排序的结果就是升序//如果我们需要进行降序排列怎么办呢?//这时候就需要用到仿函数了//我们需要通过仿函数或者函数指针来改变排序的规则for (auto& e : array)cout << e << " ";cout << endl;//用仿函数生成函数对象Greater<int> gr;std::sort(array, array + sizeof(array) / sizeof(array[0]),gr);for (auto& e : array)cout << e << " ";//用函数指针传参进行排序std::sort(array, array + sizeof(array) / sizeof(array[0]), FunGreater);//仿函数和函数指针的效果是相同的
}

        但是如果要排序的元素为自定义类型时,需要用户定义排序规则。 

struct Goods
{string _name;//名字double _price;//价格int _num;//数量Goods(const char* str, double price,int num):_name(str),_price(price),_num(num){ }
};
void TestSort1()
{vector<Goods> v1 = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };//现在需要对自定义类型进行排序//如果按照_name排序我们需要实现至少两个仿函数//按照_price排序也需要两个仿函数//按照_num排序也需要两个仿函数//这样的话就需要我们实现很多的仿函数,如果仿函数的命名风格不好的话,别人对于这些仿函是干什么的必然不好理解,从这里也可以看出好的命名风格也是很重要的
}

       现在需要对自定义类型进行排序,如果按照_name排序我们需要实现至少两个仿函数,按照_price排序也需要两个仿函数, 按照_num排序也需要两个仿函数, 这样的话就需要我们实现很多的仿函数,如果仿函数的命名风格不好的话,别人对于这些仿函是干什么的必然不好理解,从这里也可以看出好的命名风格也是很重要的。 

struct Goods
{string _name;//名字double _price;//价格int _num;//数量Goods(const char* str, double price,int num):_name(str),_price(price),_num(num){ }
};
struct ComparePriceLess
{bool operator()(const Goods &g1, const Goods &g2){return g1._price < g2._price;}
};
struct ComparePriceMore
{bool operator()(const Goods& g1, const Goods& g2){return g1._price > g2._price;}
};
struct CompareNumLess
{bool operator()(const Goods& g1, const Goods& g2){return g1._num < g2._num;}
};
struct CompareNumMore
{bool operator()(const Goods& g1, const Goods& g2){return g1._num > g2._num;}
};
struct CompareNameLess
{bool operator()(const Goods& g1, const Goods& g2){return g1._name < g2._name;//string重载了operator>和operator<所以可以直接比较}
};
struct CompareNameMore
{bool operator()(const Goods& g1, const Goods& g2){return g1._name > g2._name;}
};
void TestSort1()
{vector<Goods> v1 = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };//现在需要对自定义类型进行排序//如果按照_name排序我们需要实现至少两个仿函数//按照_price排序也需要两个仿函数//按照_num排序也需要两个仿函数//这样的话就需要我们实现很多的仿函数,如果仿函数的命名风格不好的话,别人对于这些仿函是干什么的必然不好理解,从这里也可以看出好的命名风格也是很重要的std::sort(v1.begin(), v1.end(), ComparePriceMore());std::sort(v1.begin(), v1.end(), CompareNumMore());std::sort(v1.begin(), v1.end(), CompareNameMore());}int main()
{TestSort1();return 0;
}

        为了提高代码的可读性,所以在这里引入了lambda表达式。 

2.lambda表达式语法

        上述的问题也可以使用lambda表达式来解决。

void TestSort2()
{vector<Goods> v1 = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };std::sort(v1.begin(), v1.end(), [](const Goods& g1, const Goods& g2) ->bool {return g1._name > g2._name;});std::sort(v1.begin(), v1.end(), [](const Goods& g1, const Goods& g2)->bool {return g1._num > g2._num;});std::sort(v1.begin(), v1.end(), [](const Goods& g1, const Goods& g2)->bool {return g1._price > g2._price;});
}

        实际上我们可以看到lambda表达式就是匿名函数。 

        lambda表达式的语法: 

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

        1.lambda表达式各部分说明 

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

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

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

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

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

注意:

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

        

int main()
{//最简单的lambda表达式[] {};//但是这个表达式没有意义//省略了参数列表和返回值类型返回值类型由编译器推导为intint a = 3,b = 4;[=] {return a + b;};//省略了返回值类型,无返回值类型auto fun1 = [&] (int c){b = a + c;};//由auto自动推导的一个对象fun1(10);cout << b << endl;int c = 0;//各部分都很完善的lambda表达式auto fun2 = [=, &b]()->int {return b += a + c;};//复制捕捉xint x = 10;auto add_x = [x](int a)mutable ->int {x *= 2;return a + x;};return 0;
}

         通过上述的例子可以看出实际上lambda表达式可以理解为无名函数,该函数无法直接调用,需要通过auto将其赋值给一个变量。

        2.捕捉列表说明

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

        [val]:表示值传递捕获变量val。

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

        [&val]:表示引用捕获变量val。

        [&]:表示引用捕获所有的变量(包括this)。

        [this]:表示值传递的方式捕获this指针。

        注意:

        a.父作用域指包含lambda表达式的语句块 

        b.语法上捕捉列表由多个捕捉项组成,并以逗号分隔

        比如:[=,&b,&b];以引用方式捕捉变量a和b,使用值捕捉的方式捕捉其它变量。

                   [&,a,this]:以值捕捉的方式捕捉a和this指针,以引用的捕捉方式捕捉其它的值。 

        c.捕捉列表不允许变量重复传递,否则就会导致编译错误

        比如:[=,a];以值的方式捕捉所有变量,然后又捕捉a变量重复了。

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

        e.在块作用域中的lambda表达式仅能捕捉父作用域中的局部变量,捕捉任何非此局部域的或者非局部变量都会导致编译报错

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

void (*PF)();
int main()
{auto f1 = [] {cout << "hello world" << endl;};auto f2 = [] {cout << "hello world" << endl;};//f1 = f2;//此处会编译失败,因为lambda表达式的底层编译器会将它替换成一个仿函数;auto f3(f2);//允许构造拷贝一个新的副本f3();//可以将lambda表达式赋值给相同类型的指针PF = f2;PF();return 0;
}

 

3.函数对象和lambda表达式

        函数对象又叫做仿函数,可以像函数一样使用的对象,就是在类中重载了operator()运算符的类对象。

class Rate
{
public:Rate(double rate):_rate(rate){}double operator()(double year, double money){return money * year * _rate;}
private:double _rate;
};
int main()
{//函数对象double rate = 0.90;Rate r1(rate);r1(10000, 2);//lambdaauto r2 = [=](double money, double year)->double {return money * year * rate;};r2(10000, 2);return 0;
}

        从使用方来看lambda表达式和函数对象完全一样,函数对象将rate作为其成员变量,定义时给初始值就好了,lambda表达式通过捕捉列表来对rate进行捕捉。

 

        实际上在底层编译器对lambda表达式的处理是完全按照函数对象进行处理的,即如果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载operator()。 调用lambda表达式时,编译器会给它起一个名字lambda_uuid,(唯一的名字,然后去调用它)。

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

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

相关文章

2023年MySQL实战核心技术第二篇

目录 五 . 日志系统&#xff1a;一条SQL更新语句是如何执行的&#xff1f; 5.1 解释 5.2 重要的日志模块&#xff1a;redo log 5.2.1 解释 5.2.2 WAL&#xff08;Write-Ahead Logging&#xff09; 5.2.3 crash-safe。 5.3 重要的日志模块&#xff1a;binlog 5.3 .1 为什么会有…

元素周期表-背诵元素周期表更简单

元素周期表是一款极其炫酷、简约的记忆和查看周期表元素的软件。 【软件特点】&#xff1a; ●有趣谐音速记&#xff1a;软 件内有按周期、化合价、元素符号分类使用谐音速记的小技巧。 ●3D元素周期表&#xff1a;用户可以选择按表面、球体、螺旋、网格来3D炫酷的展示元素周期…

小白备战大厂算法笔试(三)——栈、队列、双向队列

文章目录 栈栈常用操作栈的实现基于链表的实现基于数组的实现 两种实现对比栈典型应用 队列队列常用操作队列实现基于链表的实现基于数组的实现 队列典型应用 双向队列双向队列常用操作双向队列实现基于双向链表的实现基于数组的实现 双向队列应用 栈 栈是一种遵循先入后出的逻…

MySQL之用户管理

用户 用户信息 MySQL中的用户&#xff0c;都存储在系统数据库mysql的user表中 ps&#xff1a; host&#xff1a; 表示这个用户可以从哪个主机登陆&#xff0c;如果是localhost&#xff0c;表示只能从本机登陆 user&#xff1a; 用户名 authentication_string&#xff1a; 用户…

2023高教社杯数学建模D题思路分析 - 圈养湖羊的空间利用率

# 1 赛题 D 题 圈养湖羊的空间利用率 规模化的圈养养殖场通常根据牲畜的性别和生长阶段分群饲养&#xff0c; 适应不同种类、不同阶段 的牲畜对空间的不同要求&#xff0c;以保障牲畜安全和健康&#xff1b;与此同时&#xff0c;也要尽量减少空间闲置所造成 的资源浪费。在实际…

点云从入门到精通技术详解100篇-基于点云和图像的智能交通路侧感知

目录 前言 基于激光雷达和相机的路侧感知系统需求分析及设计 2.1 引言 2.2 感知系统总体结构

SpringBoot实现登录拦截

如果我们不进行登录拦截的话&#xff0c;即使我们跳过登录页面直接去访问任意一个页面也能访问成功&#xff0c;那么登录功能就没有意义&#xff0c;同时也会存在安全问题&#xff0c;因为有些操作是要用户登录后才能执行的&#xff0c;如果用户没有登录&#xff0c;该接口就获…

【数据库事务日志碎片原理分析与方案】-分析篇

前言:说都数据库的事务日志&#xff0c;可以说我们是再熟悉不过的了。一般而言&#xff0c;我们都没有必 要去关心事务日志中的虚拟日志文件的个数。这里提到的“虚拟日志文件”的概念&#xff0c;我们 后面会进行专门的讲述。很多的时候&#xff0c;我们在建立数据库的时候&am…

使用Caffeine实现帖子的缓存来优化网站的运行速度

导入依赖 <!-- https://mvnrepository.com/artifact/com.github.ben-manes.caffeine/caffeine --><dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>3.1.7</version>…

文盘Rust——起手式,CLI程序 | 京东云技术团队

技术的学习从不会到会的过程是最有意思的&#xff0c;也是体会最多的。一旦熟练了&#xff0c;知识变成了常识&#xff0c;可能就失去了记录学习过程的最佳时机。 在我看来学习一门计算机语言和学习人类语言有很多共通之处。我们学习人类语言是从单个的词开始&#xff0c;然后…

PyCharm下载安装

PyCharm下载链接 点击下载PyCharm Community Edition社区版&#xff08;PyCharm Professional专业版需要收费&#xff0c;但可以免费试用 30 天&#xff0c;也可以找到激活方式&#xff1b;而社区版是完全免费的&#xff0c;初学者学习 Python建议使用社区版&#xff0c;不会有…

降水预报之双重惩罚

在降水预报中&#xff0c;通常会出现 "双重惩罚问题 "的指标或度量包括那些常用于预报验证的指标或度量。当假阴性&#xff08;漏报降水事件&#xff09;和假阳性&#xff08;误报&#xff09;受到同等惩罚或加权时&#xff0c;就会出现双重惩罚问题&#xff0c;这在…

js异步控制器,实现异步同步代码交替穿插执行

一、js代码中发起请求&#xff0c;读取文件等操作都是异步执行&#xff0c;如果我们希望将多个异步函数的结果都收集起来&#xff0c;统一处理&#xff0c;这应该怎么做呢&#xff1f; 第一种方法&#xff0c;需要用async和await关键字将异步函数强制为同步状态&#xff0c;这…

Kafka的文件存储与稀疏索引机制

![在这里插入图片描述](https://img-blog.csdnimg.cn/dde7fc866d214985baaa87300a472578.png)这些是存储在分区(分区才是实际的存储)文件中的. seg是逻辑概念 而实际由log存储的. index是偏移量索引而timeindex是时间戳索引 log就是seg 找数据就是先找log 再从log去找

MYSQL 高级SQL语句

1、按关键字排序&#xff1a; order by 语句用来实现 &#xff0c;前面可以使用where字句使查询结果进一步过滤 asc 是按照升序排序 &#xff0c; 默认的 desc 是按照降序排序 order by的语法结构 例&#xff1a;select name,score from ku order by score desc; 表示将数…

数字图像处理-形态学图像处理

形态学图像处理 一、基础知识1.1 什么是形态学操作 二、腐蚀与膨胀2.1 腐蚀2.2 膨胀 三、开操作与闭操作3.1 开操作3.2 闭操作3.3 实验对比 四、一些基本的形态学算法4.1边界提取4.2空洞填充4.3 凸壳 一、基础知识 1.1 什么是形态学操作 数字图像处理中的形态学操作是一组用于…

Linux CPU线程绑核

为了加快程序的运行速度和充分利用CPU资源&#xff0c;我们可以人为将不同线程绑定在不同的cup上&#xff0c;例如有两个线程A,B&#xff0c;其中A已经在CPU0上运行&#xff0c;并且CPU0上还有其他的任务&#xff0c;那么我们可以将线程B绑到CPU1上&#xff0c;这样就可以减轻C…

链路追踪Skywalking快速入门

目录 1 Skywalking概述1.1 微服务系统监控三要素1.2 什么是链路追踪1.2.1 链路追踪1.2.2 OpenTracing1、数据模型&#xff1a;2、核心接口语义 1.3 常见APM系统1.4 Skywalking介绍1、SkyWalking 核心功能&#xff1a;2、SkyWalking 特点&#xff1a;3、Skywalking架构图&#x…

Medium: 9 Important Things to Remember for AB Test

There are lots of things that can go wrong [存在隐患] when you try to create AB tests. Goal & Motivation Making it clear helps align the team toward the goal. Hypothesis Your hypothesis should be short and to the point. It should not run into a coup…

直播平台源码开发搭建APP的DASH协议:流媒体技术其中一环

在直播平台源码APP中&#xff0c;有着许许多多、多种多样的功能&#xff0c;比如短视频功能&#xff0c;帮助我们去获取信息&#xff0c;看到全世界用户身边发生的事情或是他们的生活&#xff1b;又比如直播功能&#xff0c;为用户提供了实时的娱乐享受&#xff0c;还让一些用户…