【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 …

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

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

使用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()

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

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

[CKA]考试之查看pod的cpu

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

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;可能不会让应用层直接去…

我在leetcode用动态规划炒股

事情是这样的&#xff0c;突然兴起的我在letcode刷题 121. 买卖股票的最佳时机122. 买卖股票的最佳时机 II123. 买卖股票的最佳时机 III 以上三题。 1. 121. 买卖股票的最佳时机 1.1. 暴力遍历&#xff0c;两次遍历 1.1.1. 算法代码 public class Solution {public int Ma…

【Redis】——RDB快照

Redis 是内存数据库&#xff0c;但是它为数据的持久化提供了两个技术&#xff0c;一个是AOF日志&#xff0c;另一个是RDB快照&#xff1a; AOF 文件的内容是操作命令&#xff1b;RDB 文件的内容是二进制数据。 RDB 快照就是记录某一个瞬间的内存数据&#xff0c;记录的是实际…

机器学习深度学习——卷积神经网络(LeNet)

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——池化层 &#x1f4da;订阅专栏&#xff1a;机器学习&&深度学习 希望文章对你们有所帮助 卷积神…

Python Opencv实践 - 基本图像IO操作

import numpy as np import cv2 as cv import matplotlib.pyplot as plt#读取图像 #cv2.IMREAD_COLOR&#xff1a; 读取彩色图像&#xff0c;忽略alpha通道&#xff0c;也可以直接写1 #cv2.IMREAD_GRAYSCALE: 读取灰度图&#xff0c;也可以直接写0 #cv2.IMREAD_UNCHANGED: 读取…

C高级【day4】

思维导图&#xff1a; 写一个函数&#xff0c;获取用户的uid和gid并使用变量接收&#xff1a; #!/bin/bashfunction get_uid {my_uidid -umy_gidid -g }get_uid echo "当前用户的UID&#xff1a;$my_uid" echo "当前用户的GID&#xff1a;$my_gid"整理冒泡…

论文代码学习—HiFi-GAN(4)——模型训练函数train文件具体解析

文章目录 引言正文模型训练代码整体训练过程具体训练细节具体运行流程 多GPU编程main函数&#xff08;通用代码&#xff09;完整代码 总结引用 引言 这里翻译了HiFi-GAN这篇论文的具体内容&#xff0c;具体链接。这篇文章还是学到了很多东西&#xff0c;从整体上说&#xff0c…

FPGA学习——Altera IP核调用之PLL篇

文章目录 一、IP核1.1 IP核简介1.2 FPGA中IP核的分类1.3 IP核的缺陷 二、PLL简介2.1 什么是PLL2.2 PLL结构图2.3 C4开发板上PLL的位置 三、IP核调用步骤四、编写测试代码五、总结 一、IP核 1.1 IP核简介 IP核&#xff08;知识产权核&#xff09;&#xff0c;是在集成电路的可…

8-7 homework

1.思维导图 2.写一个函数&#xff0c;获取用户的uid和gid并使用变量接收 3.bubble_sort #include <stdio.h>//先排好的都是放在最后的&#xff0c;所以for的内层限制条件是不把后面的计算在内的&#xff0c;内层只循环前面的 int main(){int a [10]{11,42,3,24,65,16,73…