当前位置: 首页 > news >正文

Lambda表达式的本质

一直想写一篇文章,来总结lambda表达式,但是之前感觉总结的不是特别到位,现在看了几篇文章和视频后,感觉对lambda表达式有了比较深刻的认识,现在进行记录总结如下:

lambda表达式又叫做匿名函数,lambda表达式本质是一个匿名类,lambda为了我们能更方便的定义出一个函数方法提供了便利,下面咱们开始介绍下lambda表达式。

1.lambda表达式的引出

首先我们想完成一个   Add的函数,这个函数有个功能,就是把自己成员变量的值num_,和传进来的参数x相加,并且返回。我们一般用C++的写法如下:

#include<typeinfo>
#include<iostream>
#include<functional>
using namespace std;
class Cal{
public:
Cal(int num):num_(num){cout<<"num_ :"<<num_<<endl;
};
int Add(int x){ cout<<"x+num_:"<<x+num_<<endl;return x+num_;
};
private:const int num_;
};int main(){Cal temp(5);temp.Add(3);return 0;
}

编译并且执行程序:

zhc@ubuntu:~/test/linux$ g++ -std=c++14  test.cpp -o test && ./test 
num_ :5
x+num_:8

接下来我们用仿函数来实现,仿函数也就是重载小括号  ()的类,我们修改代码如下:

#include<memory>
#include<typeinfo>
#include<iostream>
#include<functional>
using namespace std;
class Cal{
public:
Cal(int num):num_(num){cout<<"num_ :"<<num_<<endl;
};
int operator () (int x){ cout<<"x+num_:"<<x+num_<<endl;return x+num_;
}
private:const int num_;
};
int main(){Cal Add(5);Add(3);return 0;
}

编译并且输出结果:

zhc@ubuntu:~/test/linux$ g++ -std=c++14  test.cpp -o test && ./test 
num_ :5
x+num_:8

也就是说如果我们想实现一个简单的Add函数,也要定义一个类,或者用成员函数实现,或者用仿函数实现,有没有一种写法上更简单的方式呢,省略掉类的定义,那么我们的主角就要登场了,就是lambda表达式.看如下代码:

#include<memory>
#include<typeinfo>
#include<iostream>
#include<functional>
using namespace std;
int main(){int num=5;auto lam_add=[num](int x) ->int {cout<<"num+x:"<<num+x<<endl;return num+x;};  lam_add(3);return 0;
}

是不是形式上简洁了许多,输出结果如下:

zhc@ubuntu:~/test/linux$ g++ -std=c++14  test.cpp -o test && ./test 
num+x:8

正如我们的lambda表达式结果看,这个跟我们刚才写的仿函数类也是基本相同的,实际上我写的第二个例子,就是这个lambda表达式在调用的时候,生成的临时类对象,所对应的类的定义,

注意我的成员变量的写法,const int num_,这个是我故意这么写的,因为确实等价方式就是这样。下面我们来讲一讲lambda表达式的捕获列表,以及不同捕获形式,在生成类的成员函数里的可读可写属性。

2. lambda表达式的捕获列表

lambda表达式的书写形式如下:

[captrue] (params) opt -> ret {body};
其中,capture是捕获列表;params是参数列表;
opt是函数选项(mutable,noexcept等关键字);
ret是返回值类型;body是函数体。

lambda表达式可以通过捕获列表捕获一定范围内的变量:

[]不捕获任何变量;

[bar]按值捕获bar变量,同时不捕获其他变量,并作为副本在函数体使用(按值捕获);

[&bar]按引用捕获bar变量,同时不捕获其他变量;并作为引用在函数体使用(按引用捕获);

[=]捕获外部作用域作用变量,并作为副本在函数体使用(按值捕获);

[&]捕获外部作用域所有变量,并作为引用在函数体使用(按引用捕获);

[=,&foo]按值捕获外部作用域所有变量,并按引用捕获foo变量;

[this]捕获当前类中的this指针,让lambda拥有和当前类成员函数同样的访问权限,如果已经使用了&或者=,就默认添加此选项。捕获this的目的是可以在lambda中使用当前类的成员函数和成员变量。

一般,不会捕获全局变量,静态全局变量,局部静态局部变量,静态成员变量。

下面我们按照按值捕获和按引用捕获来说明下,捕获后在生成的匿名类里的成员变量情况,与外部变量的可写属性是否相同,以及是否能够改变外部变量。

下面咱们看下具体的例子:

#include<memory>
#include<typeinfo>
#include<iostream>
#include<functional>
using namespace std;
int main(void){
int m=1;
const int n=2;
auto g = [m,n](int x)->int{
m++;
n++;
return m+n;};
int z=g(0);
return 0;
}

编译代码并且执行:编译报错如下:

zhc@ubuntu:~/test/linux$ g++ -std=c++14  test.cpp -o test && ./test 
test.cpp: In lambda function:
test.cpp:10:2: error: increment of read-only variable ‘m’m++;^~
test.cpp:11:2: error: increment of read-only variable ‘n’n++;^~

具体结论相关的东西见下面的两个图,说的更清楚一些。

 这个图是我从别的地方copy过来的,暂时粘贴这里,后期再敲试验代码。

如果我们想改变这个   m,p的可写属性,可以在    lambda表达式中加入mutalbel关键字。

我画红线的部分,是原文书写错误,应该是有mutable时。

 这里强调一点,这个  const int n,在用这个mutable以后,那么对应的成员变量还是  const int n ,也就是说这个属性,是拿 mutable改变不了的。

3.lambda表达式中的  准函数,准对象,如何接收lambda表达式

当一个lambda表达式没有捕获任何外部变量时,则可以看成一个准函数,如果捕获了外部对象,则可以看成一个准对象。具体见下面图片。

 那么对于这种准对象的情况,我们是怎么接呢,c++  11中推出了std::function   模板类,它即能接准函数的情况,也能接准对象的情况。下面我们来简单的介绍下std::function.

std::function的实例可以对任何可以调用的目标实体进行存储、复制、和调用操作,这些目标实体包括普通函数、Lambda表达式、函数指针、以及其它函数对象等。std::function对象是对C++中现有的可调用实体的一种类型安全的包裹(我们知道像函数指针这类可调用实体,是类型不安全的)。

std::function包含于头文件 #include<functional> 中,可将各种可调用实体进行封装统一,包括

  • 普通函数
  • lambda表达式
  • 函数指针
  • 仿函数(functor 重载括号运算符实现)
  • 类成员函数
  • 静态成员函数

其中具体的使用方式,这里其他的就不再举例子,因为在我的另外一个文章中有介绍。这里就说一个lambda表达式的例子:

#include<memory>
#include<iostream>
#include<functional>
#include<map>
#include<string>
using namespace std;class SaveStuInfo{
public:void SaveInfo(const string& num,std::function<string(const string&,const string&)> callback){student_info.insert(make_pair(num,callback));
}void CallLmd(const string &num){string name = "zhc";string score = "100";student_info[num](name,score);
}
private:using get_name_score = std::function<string(const string &,const string &)>;std::map<std::string, get_name_score> student_info; //num as key};int main(void){
string num="123";
//string name ="zhc";
//string score="100";
auto lmd = [](const string&name,const string&score) ->string{cout<<"lmd was called value: "<<name+score<<endl;return name+score;
};
SaveStuInfo  temp;
temp.SaveInfo(num,lmd);
temp.CallLmd(num);
return 0;
}

这里要注意一点,在temp.SaveInfo(num,lmd),这个时候不要写成,temp.SaveInfo(num,lmd(name,score));这样会报错,换句话说,std::function,里不用存储具体的lambda参数里的值,真正传递的lambda参数是在调用这个lambda表达式的时候。

编译并且执行程序结果如下:

zhc@ubuntu:~/test/linux$ g++ -std=c++14  test.cpp -o test && ./test 
lmd was called value: zhc100

相关文章:

Lambda表达式的本质

一直想写一篇文章&#xff0c;来总结lambda表达式&#xff0c;但是之前感觉总结的不是特别到位&#xff0c;现在看了几篇文章和视频后&#xff0c;感觉对lambda表达式有了比较深刻的认识&#xff0c;现在进行记录总结如下&#xff1a; lambda表达式又叫做匿名函数&#xff0c;…...

类的加载过程(生命周期)

类的加载过程(生命周期) 一、装载&#xff1a;通过一个类的全限定名获取定义此类的二进制字节流将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构在内存中生成一个代表这个类的java.lang.Class对象&#xff08;将字节码加载到内存中&#xff09;&#xff0c;作为…...

2023最新谷粒商城笔记之MQ消息队列篇(全文总共13万字,超详细)

MQ消息队列 其实队列JDK中本身就有&#xff0c;不过这种队列也只能单体服务可能会使用&#xff0c;一旦项目使用的分布式架构&#xff0c;那么一定还是需要用到一个消息中间件的。我们引入消息队列的原因就是对我们的页面相应速度再优化&#xff0c;让用户的体验更好&#xff…...

多变量线性回归模型

多变量线性回归模型 模型参数为n1维向量&#xff0c;此时模型公式为 hθ(x)θ0x0θ1x1θ2x2...θnxnh_{\theta}(x)\theta_{0}x_{0}\theta_{1}x_{1}\theta_{2}x_{2}...\theta_{n}x_{n} hθ​(x)θ0​x0​θ1​x1​θ2​x2​...θn​xn​ 可以简化为 hθ(x)θTXh_{\theta}(x)\th…...

php 基于ICMP协议实现一个ping命令

php 基于ICMP协议实现一个ping命令 网络协议是什么ICMP 协议什么是ICMP?ICMP 的主要功能ICMP 在 IPv4 和 IPv6 的封装Wireshark抓包ICMP 请求包分析PHP构建 ICMP 数据包php中的 pack & unpack 函数字节和字符packunpackICMP计算校验和步骤总结网络协议是什么 网络协议&…...

Java基本数据类型

1.概述 佛说&#xff0c;大千世界&#xff0c;无奇不有。在这个世界里&#xff0c;物种的多样性&#xff0c;遍地开花&#xff0c;同样&#xff0c;在Java的世界里&#xff0c;也有着异曲同工之妙&#xff0c;Java秉承面向对象的特性&#xff0c;必然少不了区分对象的类型&…...

English Learning - L2 语音作业打卡 Day2 2023.2.22 周三

English Learning - L2 语音作业打卡 Day2 2023.2.22 周三&#x1f48c; 发音小贴士&#xff1a;&#x1f48c; 当日目标音发音规则/技巧&#xff1a;&#x1f36d; Part 1【热身练习】&#x1f36d; Part2【练习内容】&#x1f36d;【练习感受】&#x1f353;元音[ ɑː ]&…...

45. 跳跃游戏 II

题目&#xff1a; 45. 跳跃游戏 II难度中等1974收藏分享切换为英文接收动态反馈给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 num…...

应届生Java面试50题线程篇(含解析)

什么是线程&#xff1f; 答&#xff1a;线程是操作系统能够进行运算调度的最小单位&#xff0c;是程序执行流的最小单元。在Java中&#xff0c;可以通过实现Runnable接口或继承Thread类来创建线程。 创建线程的方式有哪些?各自的优缺点是什么&#xff1f; 继承 Thread 类&…...

【数据库】第七章 数据库设计

第七章数据库设计 数据库设计概述 数据库设计的基本步骤 需求分析概念结构设计逻辑结构设计物理结构设计数据库实施数据库运行和维护 需求分析 收集需求&#xff0c;理解需求 收集各个角色的需求 概念数据库设计 建立概念模型 &#xff0c;E-R图/IDEF1x图 消除冲突&…...

Burp Suite 常用模块简介

Burp Suite 常用模块分为 目标站点(target)模块 代理(proxy)模块 攻击(Intruder)模块 重放(Repeater) 模块 Target模块是对站点资源的收集&#xff0c;与站点各资源包发出和相应包的记录 Proxy模块是核心模块&#xff0c;可以拦截数据包发送往浏览器&#xff0c;进行修改后再…...

QML Item和Rectangle详解

1.Item和Rectangle Item类型是Qt Quick中所有可视项的基本类型。 Qt Quick中的所有可视项都继承Item。尽管Item对象没有视觉外观&#xff0c;但它定义了视觉项中常见的所有属性&#xff0c;例如x和y位置、宽度和高度、锚定和键处理支持。 Rectangle继承自Item&#xff0c;多…...

常见前端基础面试题(HTML,CSS,JS)(六)

GET 和 POST 的区别 从 http 协议的角度来说&#xff0c;GET 和 POST 它们都只是请求行中的第一个单词&#xff0c;除了语义不同&#xff0c;其实没有本质的区别。 之所以在实际开发中会产生各种区别&#xff0c;主要是因为浏览器的默认行为造成的。 受浏览器的影响&#xf…...

深度学习 李沐报错

3.6. softmax回归的从零开始实现 — 动手学深度学习 2.0.0 documentation softmax从0开始实现 函数执行需要加main指定 改成这样 if __name__"__main__":print(evaluate_accuracy(net, test_iter)) 不然会这样出错 RuntimeError: An attempt has been m…...

【JAVA程序设计】(C00104)基于Springboot的家庭理财管理系统——有文档

基于Springboot的家庭理财管理系统项目简介项目获取开发环境项目技术运行截图运行视频项目简介 基于Springboot开发的家庭理财管理系统设计与实现共分为三个角色&#xff1a;系统管理员、家庭管理员、家庭用户 管理员角色包含以下功能&#xff1a; 用户管理、修改密码、角色管…...

【第五章 AOP概述,底层原理,AOP术语,切入点表达式,AOP操作(基于注解方式,基于xml配置文件)】

第五章 AOP概述&#xff0c;底层原理&#xff0c;AOP术语&#xff0c;切入点表达式&#xff0c;AOP操作&#xff08;基于注解方式&#xff0c;基于xml配置文件&#xff09; 1.AOP概述&#xff1a; &#xff08;1&#xff09;什么是AOP&#xff1a; ①面向切面编程&#xff08;…...

面试官: 你知道 JWT、JWE、JWS 、JWK嘛?

想起了 之前做过的 很多 登录授权 的项目 它相比原先的session、cookie来说&#xff0c;更快更安全&#xff0c;跨域也不再是问题&#xff0c;更关键的是更加优雅 &#xff0c;所以今天总结了一篇文章来介绍他 JWT 指JSON Web Token&#xff0c;如果在项目中通过 jjwt 来支持 J…...

基于企业微信应用消息的每日早安推送

基于企业微信应用消息的每日早安推送 第一步&#xff1a;注册企业微信 企业微信注册地址&#xff1a;https://work.weixin.qq.com/wework_admin/register_wx 按照正常流程填写信息即可&#xff0c;个人也可以注册企业微信&#xff0c;不需要公司 注册完成后&#xff0c;登录…...

【数字IC基础】黑盒验证、白盒验证、 灰盒验证

文章目录 一、黑盒验证二、白盒验证三、灰盒验证一、黑盒验证 1、黑盒验证:大多数基于仿真的验证环境都是黑盒验证;2、不需要知道设计的内部结构和特性,只需要在输入端口打激励,观察输出即可;3、验证工程师学习设计的规格,然后编写验证环境中的 drivers, monitors, check…...

管理的本质是达成目标

“没有目标&#xff0c;其实就没有管理学存在的意义。要有效地使用管理学的智慧&#xff0c;首先要建立清晰的目标。” - 《宁向东的管理学课》 起源 最近开始刷很久之前就在得到上买了的已经起灰了的课程&#xff0c;看到这句话觉得很有道理。 思考 这里面有一个很重要的词…...

生成xcframework

打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库&#xff0c;特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器

——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的​​一体化测试平台​​&#xff0c;覆盖应用全生命周期测试需求&#xff0c;主要提供五大核心能力&#xff1a; ​​测试类型​​​​检测目标​​​​关键指标​​功能体验基…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

微服务商城-商品微服务

数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作&#xff1a;ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等&#xff08;ArcGIS出图图例8大技巧&#xff09;&#xff0c;那这次我们看看ArcGIS Pro如何更加快捷的操作。…...