【C++】Lambda表达式的使用

学习目标:

例如:

  • 了解Lambda的优点
  • 掌握Lambda表达式的使用
  • 了解Lambda表达式的底层原理

学习内容:

  1. Lambda表达式的语法

文章目录

  • 学习目标:
  • 学习内容:
  • Lambda表达式
  • 排序案例
  • Lambda表达式语法
  • 捕捉列表
  • Lambda表达式模拟

Lambda表达式

lambda表达式的底层实现涉及到闭包(Closure)的概念。闭包是一个函数对象,它可以捕获外部作用域中的变量,并在其生命周期内访问和修改这些变量。lambda表达式的底层实现就是通过创建闭包来实现的。

具体而言,lambda表达式在底层会被转化为一个函数对象。这个函数对象中包含了捕获的外部变量,并且重载了函数调用运算符operator()。函数对象可以像普通的函数一样被调用,其执行的代码就是lambda表达式中的代码。

lambda表达式的展开过程包括以下几个步骤:

  1. 语法解析:将lambda表达式解析为函数对象的声明和定义。
  2. 生成函数对象:根据lambda表达式的参数、返回类型和捕获列表等信息,生成一个函数对象。
  3. 生成仿函数类:根据生成的函数对象,生成一个仿函数类(Functor),其中重载了函数调用运算符operator()
  4. 类型推导:根据lambda表达式中的代码和上下文,进行类型推导,确定函数对象的参数类型和返回类型。
  5. 生成代码:根据类型推导的结果,生成调用函数对象的代码。
  6. 调用lambda表达式:通过调用函数对象的operator(),执行lambda表达式中的代码。

排序案例

对于一个简单的数组来说:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;int main(void)
{vector<int>arr = { 9,8,5,6,3,2,1,5,12,13,14,520 };sort(arr.begin(), arr.end());for (auto& e : arr){cout << e << " ";e++;}cout << endl;sort(arr.begin(), arr.end(), greater<int>());for (auto& e : arr){cout << e << " ";e++;}return 0;
}

这样排序和简单,但是实际开发当中都是在类当中排序,如下。

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;struct Goods
{string _name;  // 名字double _price; // 价格int _evaluate; // 评价Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){}
};
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;}
};int main(void)
{vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };sort(v.begin(), v.end(), ComparePriceGreater());for (auto& e : v){cout << e._name<<" "<<e._price<<" "<<e._evaluate<<endl;}cout << "==============================" << endl;sort(v.begin(), v.end(), ComparePriceLess());for (auto& e : v){cout << e._name << " " << e._price << " " << e._evaluate << endl;}return 0;
}

每一种排序方式都要写一个类,然后重载operator(),这样是不是有点太麻烦了,因此,C++11增加了Lambda表达式,来简化这一过程。

如下是Lambda表达式的使用样例:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;struct Goods
{string _name;  // 名字double _price; // 价格int _evaluate; // 评价Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){}
};int main(void)
{vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };sort(v.begin(), v.end(), [](Goods& s1, Goods& s2)->bool{return s1._price < s2._price;});for (auto& e : v){cout << e._name<<" "<<e._price<<" "<<e._evaluate<<endl;}cout << "==============================" << endl;sort(v.begin(), v.end(), [](Goods& s1, Goods& s2)->bool {return s1._price > s2._price;});for (auto& e : v){cout << e._name << " " << e._price << " " << e._evaluate << endl;}return 0;
}

其实Lambda表达式就是一个匿名函数。

Lambda表达式语法

语法格式:[捕捉列表](参数列表)mutable->返回类型{函数体}

捕捉列表能够捕捉当前栈帧的所有对象(全局的也可以捕捉到)
参数列表和函数中的参数列表一样,int a,int b这些
mutable默认情况下Lambda表达式是一个const函数,不能被修改,而mutable可以取消const,可以对参数进行修改
返回类型返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回 值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推 导。
函数体函数的具体实现,在该函数体内,除了可以使用其参数外,还可以使用所有捕获 到的变量。

C++中最简单的Lambda表达式:[]{},但它并不能干什么。

#include<iostream>
using namespace std;int main(void)
{int a = 1, b = 2,c=5;auto func1=[=] {return a + b; };cout << func1() << endl;auto func2 = [=](int a, int b) {return a + b + c; };cout << func2(a,b)<<endl;auto fun2 = [=, &b](int c)->int {return b += a + c; };cout << fun2(10) << endl;return 0;
}

捕捉列表

lambda函数在捕获外部变量时会创建一个闭包。闭包是指一个函数对象,它包含了函数定义时的环境信息,包括捕获的外部变量。通过捕获外部变量,lambda函数可以在其定义的作用域之外使用这些变量。闭包的存在使得lambda函数可以延长外部变量的生命周期,并且可以在函数调用结束后仍然访问这些变量。这是lambda函数的一个非常强大的特性。

[var]捕捉值传递变量var
[=]表达值传递的所有变量(当前栈帧)
[&var]捕捉引用传递变量var
[&]捕捉引用传递的所有变量(当前栈帧)、包括this
[this]捕捉当前的this

Tip🎉:

1.语法上捕捉列表可由多个捕捉项组成,并以逗号分割。

比如:[=, &a, &b]:以引用传递的方式捕捉变量a和b,值传递方式捕捉其他所有变量 [&,a, this]:值传递方式捕捉变量a和this,引用方式捕捉其他变量

2.捕捉列表不允许变量重复传递,否则就会导致编译错误。 比如:[=, a]:=已经以值传递方式捕捉了所有变量,捕捉a重复。

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

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

void (*PF)();
int main()
{auto f1 = []{cout << "hello world" << endl; };auto f2 = []{cout << "hello world" << endl; };// 此处先不解释原因,等lambda表达式底层实现原理看完后,大家就清楚了//f1 = f2;   // 编译失败--->提示找不到operator=()// 允许使用一个lambda表达式拷贝构造一个新的副本auto f3(f2);f3();// 可以将lambda表达式赋值给相同类型的函数指针PF = f2;PF();return 0;
}

Lambda表达式模拟

	auto print = [] {cout << "hello world"; };print();

比如这样的一个表达式,机器会自动翻译为如下的代码:

	class __lambda_Print_123{public:void operator()(void){cout << "hello world" << endl;}};

还有这样的

	int a = 5, b = 2;auto Add = [](int a, int b)->int {return a + b; };

会翻译成如下代码:

	class __lambda_Add_123 {public:int operator()(int a, int b) const {return a + b;}};

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

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

相关文章

Javascript 数据结构[入门]

作者&#xff1a;20岁爱吃必胜客&#xff08;坤制作人&#xff09;&#xff0c;近十年开发经验, 跨域学习者&#xff0c;目前于海外某世界知名高校就读计算机相关专业。荣誉&#xff1a;阿里云博客专家认证、腾讯开发者社区优质创作者&#xff0c;在CTF省赛校赛多次取得好成绩。…

3年经验,面试测试岗只会功能测试开口要求18K,令我陷入沉思。

由于朋友临时有事&#xff0c; 所以今天我代替朋友进行一次面试&#xff0c;公司需要招聘一位自动化测试工程师&#xff0c;我以很认真负责的态度完成这个过程&#xff0c; 大概近30分钟。 主要是技术面试&#xff0c; 在近30分钟内&#xff0c; 我与被面试者是以交流学习的方式…

java linq多字段排序时间比较

public static void main(String[] args) {//100万条数据List<CrmInvestSaleUserCount> waitAssignUserList new ArrayList<>();for (int i 0; i < 1000000; i) {waitAssignUserList.add(new CrmInvestSaleUserCount().setSales_username("test" i…

架构训练营学习笔记:6-2 微服务基础选型

基础选型 微服务基础设施架构 优先级 其中&#xff0c;核心 就是服务注册、服务发现、服务路由。 模式1-嵌入SDK 模式2-反向代理式 模式3-网络代理式&#xff08;Service Mesh&#xff09; 模式对比 常见微服务框架选择 嵌入SDK-dubbo Spring Cloud 反向代理式 APISIX …

小研究 - 基于 SpringBoot 微服务架构下前后端分离的 MVVM 模型(一)

本文主要以SpringBoot微服务架构为基础&#xff0c;提出了前后端分离的MVVM模型&#xff0c;并对其进行了详细的分析以及研究&#xff0c;以此为相关领域的工作人员提供一定的技术性参考。 目录 1 研究背景 2 SpringBoot微服务优势 3 微服务 3.1 技术发展 3.2 技术优势 在…

流数据湖平台Apache Paimon(五)集成 Spark 引擎

文章目录 第4章 集成 Spark 引擎4.1 环境准备4.2 Catalog4.2.1 文件系统4.2.2 Hive 4.3 DDL4.3.1 建表4.3.2 修改表 第4章 集成 Spark 引擎 4.1 环境准备 Paimon 目前支持 Spark 3.4、3.3、3.2 和 3.1。课程使用的Spark版本是3.3.1。 1&#xff09;上传并解压Spark安装包 t…

MyBatis枚举映射类讨论

前言 本篇需要对于MyBatis有一定的认识&#xff0c;而且只是针对于TypeHandler接口来讨论&#xff0c;暂不讨论其他方面的问题 TypeHandler概叙 TypeHandler是MyBatis设计的一个用于参数的接口&#xff0c;你们会不会很好奇MyBatis是如何把整形&#xff0c;时间&#xff0c;字符…

模版下载和Excel文件导入

模版下载 模版下载 模版下载 /*** 生成模版** param* return AppResponse*/public AppResponse ExcelFile() throws IOException {// 创建一个新的Excel工作簿Workbook workbook new XSSFWorkbook();// 创建一个工作表Sheet sheet workbook.createSheet("页面拨测模板&…

C++类的定义和对象的创建

一、问题引入 C类和对象到底是什么意思&#xff1f; 1、C 中的类&#xff08;Class&#xff09;可以看做C语言中结构体&#xff08;Struct&#xff09;的升级版。结构体是一种构造类型&#xff0c;可以包含若干成员变量&#xff0c;每个成员变量的类型可以不同&#xff1b; …

2023-08-06力扣今日二题

链接&#xff1a; 剑指 Offer 09. 用两个栈实现队列 题意&#xff1a; 如题 解&#xff1a; 第一个栈逆序栈&#xff0c;存储插入顺序&#xff0c;另一个栈正序栈负责弹出数据 优化思想&#xff1a;只有当st2正序栈为空时才将st1逆序栈的转移过来&#xff08;若st2不为空…

使用langchain与你自己的数据对话(五):聊天机器人

之前我已经完成了使用langchain与你自己的数据对话的前四篇博客&#xff0c;还没有阅读这四篇博客的朋友可以先阅读一下&#xff1a; 使用langchain与你自己的数据对话(一)&#xff1a;文档加载与切割使用langchain与你自己的数据对话(二)&#xff1a;向量存储与嵌入使用langc…

【探索Linux】—— 强大的命令行工具 P.2(Linux下基本指令)

前言 前面我们讲了C语言的基础知识&#xff0c;也了解了一些数据结构&#xff0c;并且讲了有关C的一些知识&#xff0c;也相信大家都掌握的不错&#xff0c;今天博主将会新开一个Linux专题&#xff0c;带领大家继续学习有关Linux的内容。今天第一篇文章博主首先带领大家了解一下…

uniapp两个单页面之间进行传参

1.单页面传参&#xff1a;A --> B url: .....?code JSON.stringify(param), 2.单页面传参B–>Auni.$emit() uni.$on()

Python爬虫——解析_jsonpath解析淘票票网站

jsonpath简单解析淘票票网站&#xff0c;获取城市名称 代码如下&#xff1a; import urllib.request import json import jsonpathurl https://dianying.taobao.com/cityAction.json?activityId&_ksTS1691330599914_108&jsoncallbackjsonp109&actioncityAction&…

使用HTTP隧道时如何应对目标网站的反爬虫监测?

在进行网络抓取时&#xff0c;我们常常会遇到目标网站对反爬虫的监测和封禁。为了规避这些风险&#xff0c;使用代理IP成为一种常见的方法。然而&#xff0c;如何应对目标网站的反爬虫监测&#xff0c;既能保证数据的稳定性&#xff0c;又能确保抓取过程的安全性呢&#xff1f;…

【学习笔记】[SDOI2017] 硬币游戏

抽象&#x1f605; 我忍不了了&#xff0c;直接上概率生成函数&#x1f605; 首先要做过这道题 [CTSC2006] 歌唱王国 设 F i ( x ) ∑ f j x j F_i(x)\sum f_jx^j Fi​(x)∑fj​xj&#xff0c;其中 f j f_j fj​表示 ∣ T ∣ j |T|j ∣T∣j时第 i i i个人获胜的概率 设 …

[CKA]考试之查看pod的cpu

由于最新的CKA考试改版&#xff0c;不允许存储书签&#xff0c;本博客致力怎么一步步从官网把答案找到&#xff0c;如何修改把题做对&#xff0c;下面开始我们的 CKA之旅 题目为&#xff1a; Task 找出标签是namecpu-loader的Pod&#xff0c;并过滤出使用CPU最高的Pod&#…

用python实现猜数字游戏

1 问题 如何来判断玩家输入的数据类型来避免报错&#xff1f; 解决&#xff1a; 使用isdigit函数来判断玩家输入的数据类型是否为数字&#xff0c;是则继续运行反之则提醒玩家输入的内容不合法。 如何限制玩家输入字符的数量&#xff1f; 解决&#xff1a;定义一个最大常量和最…

Spring Boot集成Mybatis-Plus

Spring Boot集成Mybatis-Plus 1. pom.xml导包 <!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--mysql驱动--><dependency><groupId>mysql<…

论 SoC上的Linux如何拉动外部I/O

在MCU中&#xff08;如classic autosr或其他RTOS&#xff09;&#xff0c;一般可以直接通过往对应的寄存器&#xff08;地址转为指针&#xff09;写值&#xff0c; 或者调用一些硬件抽象层或者驱动接口来拉动芯片提供的GPIO。 但是在Linux中&#xff0c;可能不会让应用层直接去…