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.rhkb.cn/news/124305.html

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

相关文章

【MySQL】MySQL的安装,登录,配置和相关命令

文章目录 前言一. 卸载不需要的环境二. 获取MySQL的yum源三. 安装MySQL和启动四. 尝试登录MySQL方法1&#xff1a;获取临时root密码方法2&#xff1a;没有密码方法3&#xff1a;配置文件 五. 简单配置结束语 前言 本篇文章是基于云服务器&#xff1b;Linux&#xff1a;Centos7…

在 Windows 上远程对 Linux 进行抓包

文章目录 名词解释事先准备下载安装 Wireshark下载运行 libpcap设置 libpcap 环境变量在 Wireshark 中远程连接 libpcap 笔者的运行环境&#xff1a;&#xff08;成功&#xff09; 本地客户端&#xff1a; Windows&#xff1a; Windows 10 教育版&#xff08;本文&#xff09; …

【文末送书】全栈开发流程——后端连接数据源(二)

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★ React从入门到精通★ ★前端炫酷代码分享 ★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff…

【python手写算法】逻辑回归实现分类(含公式推导)

公式推导&#xff1a; 代码实现&#xff1a; # codingutf-8 import matplotlib.pyplot as plt import numpy as npdef f(w1,x1,w2,x2,b):zw1*x1w2*x2breturn 1/(1np.exp(-z)) if __name__ __main__:X1 [12.46, 0.25, 5.22, 11.3, 6.81, 4.59, 0.66, 14.53, 15.49, 14.43,2.1…

swiper删除虚拟slide问题

在存在缓存的情况下&#xff0c;删除较前的slide&#xff0c;会出现当前slide与后一个slide重复出现的情况 假设当前存在5个slide&#xff0c;且这5个slide已缓存&#xff0c;则删除slide2后&#xff0c;仍为5个slide&#xff0c;且slide2的内容变为slide3的内容&#xff0c;此…

JAVA设计模式第十讲:SPI - 业务差异解决方案

JAVA设计模式第十讲&#xff1a;SPI - 业务差异解决方案 我们需要在不修改源代码的情况下&#xff0c;动态为程序提供一系列额外的特性。首先想到的是Spring的AOP技术来构建应用插件&#xff0c;但是在Java自带的插件中&#xff0c;就有完整的实现。SPI&#xff08;Service Pro…

23062C++QT day2

封装一个结构体&#xff0c;结构体中包含一个私有数组&#xff0c;用来存放学生的成绩&#xff0c;包含一个私有变量&#xff0c;用来记录学生个数&#xff0c; 提供一个公有成员函数&#xff0c;void setNum(int num)用于设置学生个数 提供一个公有成员函数&#xff1a;void…

Vue3实战06-CompositionAPI+<script setup>好在哪?

Vue 3 的Composition API <script setup>这种最新的代码组织方式。

天翼云不做备案接入,如何绑定域名,不用80端口,443端口。

443&#xff0c;80端口不开启。 第一步&#xff1a; 宝塔更改web端口 搞个复杂的端口。 第二步&#xff1a; 在天翼云策略组上面开启修改过的web端口。 第三步&#xff1a;接入cdn&#xff0c;端口改成修改过的端口。

关于MyBatisPlus框架下出现xml里面定义的方法无法被正确识别以及提示调用mysql存储过程时参数无效的问题

第一个问题&#xff1a;xml里面明明定义了方法A&#xff0c;但是通过IService接口调用A的时候&#xff0c;总提示无法将接口中定义的函数绑定到xml中的同名方法中&#xff08;“Invalid bound statement (not found): com.aircas.sqlservice.mapper.SysTempIndexMapper.getRemo…

3D封装技术发展

长期以来&#xff0c;芯片制程微缩技术一直驱动着摩尔定律的延续。从1987年的1um制程到2015年的14nm制程&#xff0c;芯片制程迭代速度一直遵循摩尔定律的规律&#xff0c;即芯片上可以容纳的晶体管数目在大约每经过18个月到24个月便会增加一倍。但2015年以后&#xff0c;芯片制…

H.264视频编码推荐的分辨率和码率配置表

Video Encoding Settings for H.264 Excellence 针对H.264编码格式&#xff0c;根据不同分辨率&#xff0c;推荐其对应的码率配置关系如下图所示&#xff1a; 如下为上限&#xff0c;超过这个上限再增加码率基本无太大意义&#xff01;根据业务场景、帧率&#xff0c;建议码率…

Redis 7 第八讲 集群模式(cluster)架构篇

集群架构 Redis 集群架构图 集群定义 Redis 集群是一个提供在多个Redis节点间共享数据的程序集;Redis集群可以支持多个master 应用场景 Redis集群支持多个master,每个master又可以挂载多个slave读写分离支持数据的高可用支持海量数据的读写存储操作集群自带Sentinel的故障…

CSS水平垂直居中方案

1 前言 水平居中、垂直居中是前端面试百问不厌的问题。 其实现方案也是多种多样&#xff0c;常叫人头昏眼花。 水平方向可以认为是内联方向&#xff0c;垂直方向认为是块级方向。 下面介绍一些常见的方法。 <div class"container"><span class"inne…

Java反序列化之CommonsCollections CC1链分析

前言 cc链的研究可以说是非常适合java代码审计的入门篇了&#xff0c;十分考验java代码功力&#xff0c;其实也是基础功&#xff0c;跨过了这个门槛&#xff0c;在看看其他业务代码就会比较轻松了。不要说代码难&#xff0c;看不懂&#xff0c;作者也是刚入门java没几个月的小…

Python UI自动化 —— 关键字+excel表格数据驱动

步骤&#xff1a; 1. 对selenium进行二次封装&#xff0c;创建关键字的库 2. 准备一个表格文件来写入所有测试用例步骤 3. 对表格内容进行读取&#xff0c;使用映射关系来对用例进行调用执行 4. 执行用例 1. 对selenium进行二次封装&#xff0c;创建关键字的库 from time imp…

Cortex-A7 架构

参考《 Cortex-A7 Technical ReferenceManua.pdf 》和《 ARM Cortex-A(armV7) 编程手 册 V4.0.pdf 》 【 正点原子】I.MX6U嵌入式Linux驱动开发指南V1.6学习 1.Cortex-A7 MPCore 简介 I.MX6UL 使用的是 Cortex-A7 架构&#xff0c;Cortex-A7 MPcore 处理器支持 1~4 核&#…

kubeadm 安装k8s

目录 安装k8s 环境准备 所有节点&#xff0c;关闭防火墙规则&#xff0c;关闭selinux&#xff0c;关闭swap交换&#xff08;必须关闭swap分区&#xff09; //修改主机名 //所有节点修改hosts文件 //调整内核参数 所有节点安装docker 所有节点安装kubeadm&#xff0c;kube…

Layui快速入门之第一节Layui的基本使用

目录 一&#xff1a;Layui的基本概念 二&#xff1a;Layui使用的基本步骤 1.在官网下载layui的基本文件&#xff0c;引入css和js文件 ①&#xff1a;普通方式引入 ②&#xff1a;第三方 CDN 方式引入 2.在script标签体中编写代码 3.测试 一&#xff1a;Layui的基本概念 …

Cmake入门(一文读懂)

目录 1、Cmake简介2、安装CMake3、CMakeLists.txt4、单目录简单实例4.1、CMakeLists.txt4.2、构建bulid内部构建外部构建 4.3、运行C语言程序 5、多目录文件简单实例5.1、根目录CMakeLists.txt5.2、源文件目录5.3、utils.h5.4、创建build 6、生成库文件和链接外部库文件7、注意…