114 C++ lambda表达式捕获模式的陷阱分析和展示

一 捕获列表中的 &

捕获外部作用域中的所有变量,(不包括静态变量,静态变量不需要捕获),并作为引用在lambda表达式中使用

按照引用这种捕获方式,会导致lambda表达式包含绑定到局部变量的引用。

问题发生的点是这样:lambda表达式使用了外部的变量&,但是这个变量失效了。

问题代码

//我们还定义一个全局变量vector,这vector中的每一个元素都是function,且要求这个function的返回值是bool,参数是intstd::vector<std::function<bool(int)>> g_vec;void func138() {//调用这个,让每次产生的随机数都不一样。srand((unsigned)time(NULL));int suijishu = rand() % 6 + 1 ;//rand() % 6 会产生一个0-5的随机数,因此suijishu的值为1-6//我们在这个方法中给 g_vec存储一个数据g_vec.push_back([&](int tv) {if (tv%suijishu==0) {//如果suijishu是tv的整数倍return true;}return false;});
}void main() {func138();//1.先调用func138函数,在这一句代码执行完毕后,已经给g_vec 塞一个数据。g_vec[0](10);//2.既然g_vec已经有了数据了,我们就能使用这个数据了,已知这个数据是 可调用对象,这个可调用对象就lambda表达式,需要一个int参数,返回一个bool//然后我们debug的时候发现,调用func138()函数的时候,suijizhi=5的,但是当执行g_vec[0](10)的时候,suijizhi变成乱码了//这是因为,当调用func138()函数的时候,suijizhi是用引用传递的,假设是5//当func138执行完毕的时候,这个suijizhi的生命周期就结束了,就不是5了。//因此当我们执行 g_vec[0](10)的时候,suijizhi就真的变成随机值了。不在是5了}

简单类型可以使用的fix方案:使用值 替换 引用

但是这不是最终方案,我们这里可以使用 值替换,是因为该例子中,suijizhi是一个简单的int,那么如果是 指针呢?甚至更复杂的情况呢?在这些情况下,此方案不行

std::vector<std::function<bool(int)>> g_vec;void func139() {//调用这个,让每次产生的随机数都不一样。srand((unsigned)time(NULL));int suijishu = rand() % 6 + 1;//rand() % 6 会产生一个0-5的随机数,因此suijishu的值为1-6//我们在这个方法中给 g_vec存储一个数据g_vec.push_back([=](int tv) {if (tv%suijishu == 0) {//如果suijishu是tv的整数倍return true;}return false;});
}
void main() {func139();//1.先调用func139函数,在这一句代码执行完毕后,已经给g_vec 塞一个数据。g_vec[0](10);//2.既然g_vec已经有了数据了,我们就能使用这个数据了,已知这个数据是 可调用对象,这个可调用对象就lambda表达式,需要一个int参数,返回一个bool//然后我们debug的时候发现,调用func139()函数的时候,suijizhi=1的,且这个1这个值就直接进去到lambda表达式里面了//再次执行 g_vec[0](10)的时候,suijizhi的值还是1
}

二 形参列表可以使用 auto C++14 中允许 lambda表达式中的形参是auto 类型的,C++编译器可以自动推断,但是这个有啥用呢?好像也没用处。

注意,形参的地方写成auto 了。

void func139() {//调用这个,让每次产生的随机数都不一样。srand((unsigned)time(NULL));int suijishu = rand() % 6 + 1;//rand() % 6 会产生一个0-5的随机数,因此suijishu的值为1-6//我们在这个方法中给 g_vec存储一个数据g_vec.push_back([=](auto tv) {if (tv%suijishu == 0) {//如果suijishu是tv的整数倍return true;}return false;});
}

三 成员变量的捕获问题

问题现象,概念理清,问题原因

std::vector<std::function<bool(int)>> g_vec;
//成员捕获问题class Teacher140 {
public:int m_tempvalue = 5;void addItem() {//这里中括号里面写的是=,按照我们之前的学习,会认为这里是 按值捕获的m_tempvalue//实际上,这里有个概念是错误的, =// 等号相当于 this,g_vec.push_back([=](auto val) {cout << "m_tempvalue = " << m_tempvalue <<  endl;if (val % m_tempvalue == 0) {return true;}return false;});}
};
int mmage = 90;void main() {//如下是正常的调用//Teacher140 *ptea = new Teacher140;//ptea->addItem();//cout << g_vec[0](10) << endl;//delete ptea;////按照我们之前的理解, =是值copy,因此只要执行了ptea->addItem();m_tempvalue的值5,就会有一份copy到了lambda表达式中//因此delete pate 和 g_vec[0](10) 那个先执行无所谓,试一试Teacher140 *ptea1 = new Teacher140;ptea1->addItem();delete ptea1;cout << g_vec[0](10) << endl;//结果如下://m_tempvalue = -572662307//0//先写结论://lambda表达式的执行正确与否,取决于ptea1 对象是否存在,只有ptea1对象存在,这个lambda表达式才正确。//纠正一些概念:以及问题点分析//捕获这种概念,只针对于在创建lambda表达式的作用域内可见的非静态的局部变量,包括形参//那么m_tempvalue 符合上述定义吗?显然不符合,m_tempvalue是Teacher140的成员变量呀。//也就是说:我们实际上无法捕获到m_tempvalue的。因此也不存在值copy一份到lambda表达式的说法。//实际上 这个 等号,捕获的是this指针的值,由于有了this指针,m_tempvalue才可以访问。也就是说所有的成员变量 都是依赖于 this指针访问的,m_tempvalue //那么这样就能理通了,捕获的是this,也就是说还是依赖于 ptea1,那么将ptea1 delete了,再次执行g_vec[0](10)的时候,由于ptea1不存在了,m_tempvalue也就不存在了,问题发生}

解决方案

好,我们现在知道了问题的原因了,那么怎么改动呢?

当前case下,是有办法的,既然问题中的 成员变量捕获不到,我们让它能捕获到不就好了吗?

那么什么能被捕获呢?显然,局部变量就行,添加一个局部变量mcopyvalue ,将m_tempvalue的值赋值给 mcopyvalue,就OK了

int mcopyvalue = m_tempvalue;

注意的是:当前case 是一个int ,如果是个指针,或者一个其他类类型呢?写的要复杂的多。还有可能是不行的,比如指针,仅仅copy指针的值,肯定是不行的,内存也要copy。内存copy 了放在哪里呢?

std::vector<std::function<bool(int)>> g_vec;
//成员捕获问题class Teacher140 {
public:int m_tempvalue = 5;//void addItem() {//	//这里中括号里面写的是=,按照我们之前的学习,会认为这里是 按值捕获的m_tempvalue//	//实际上,这里有个概念是错误的, =//	// 等号相当于 this,//	g_vec.push_back([=](auto val) {//		cout << "m_tempvalue = " << m_tempvalue <<  endl;//		if (val % m_tempvalue == 0) {//			return true;//		}//		return false;//	});//}void addItem() {int mcopyvalue = m_tempvalue;g_vec.push_back([mcopyvalue](auto val) {cout << "mcopyvalue = " << mcopyvalue << endl;if (val % mcopyvalue == 0) {return true;}return false;});}
};void main() {//如下是正常的调用//Teacher140 *ptea = new Teacher140;//ptea->addItem();//cout << g_vec[0](10) << endl;//delete ptea;////按照我们之前的理解, =是值copy,因此只要执行了ptea->addItem();m_tempvalue的值5,就会有一份copy到了lambda表达式中//因此delete pate 和 g_vec[0](10) 那个先执行无所谓,试一试Teacher140 *ptea1 = new Teacher140;ptea1->addItem();delete ptea1;cout << g_vec[0](10) << endl;//结果如下://m_tempvalue = -572662307//0//先写结论://lambda表达式的执行正确与否,取决于ptea1 对象是否存在,只有ptea1对象存在,这个lambda表达式才正确。//纠正一些概念:以及问题点分析//捕获这种概念,只针对于在创建lambda表达式的作用域内可见的非静态的局部变量,包括形参//那么m_tempvalue 符合上述定义吗?显然不符合,m_tempvalue是Teacher140的成员变量呀。//也就是说:我们实际上无法捕获到m_tempvalue的。因此也不存在值copy一份到lambda表达式的说法。//实际上 这个 等号,捕获的是this指针的值,由于有了this指针,m_tempvalue才可以访问。也就是说所有的成员变量 都是依赖于 this指针访问的,m_tempvalue //那么这样就能理通了,捕获的是this,也就是说还是依赖于 ptea1,那么将ptea1 delete了,再次执行g_vec[0](10)的时候,由于ptea1不存在了,m_tempvalue也就不存在了,问题发生}

四。广义lambda 捕获

在C++14提出了 广义 lambda 捕获,实际上就是解决上面这个问题的,而且写法更简单,

目前没有测试过,是否能解决上述提出的疑问,就是如果不是int这种基本类型,是指针什么的,这个广义 lambda捕获是否能fix

核心就红色部分

void addItem() {
        g_vec.push_back([abc = m_tempvalue](auto val) {
            cout << "abc = " << abc << endl;
            if (val % abc == 0) {
                return true;
            }
            return false;
        });
    }

std::vector<std::function<bool(int)>> g_vec;
//成员捕获问题class Teacher140 {
public:int m_tempvalue = 5;//void addItem() {//	//这里中括号里面写的是=,按照我们之前的学习,会认为这里是 按值捕获的m_tempvalue//	//实际上,这里有个概念是错误的, =//	// 等号相当于 this,//	g_vec.push_back([=](auto val) {//		cout << "m_tempvalue = " << m_tempvalue <<  endl;//		if (val % m_tempvalue == 0) {//			return true;//		}//		return false;//	});//}//void addItem() {//	int mcopyvalue = m_tempvalue;//	g_vec.push_back([mcopyvalue](auto val) {//		cout << "mcopyvalue = " << mcopyvalue << endl;//		if (val % mcopyvalue == 0) {//			return true;//		}//		return false;//	});//}//C++14提出的广义lambda 捕获方案void addItem() {g_vec.push_back([abc = m_tempvalue](auto val) {cout << "abc = " << abc << endl;if (val % abc == 0) {return true;}return false;});}
};void main() {//如下是正常的调用//Teacher140 *ptea = new Teacher140;//ptea->addItem();//cout << g_vec[0](10) << endl;//delete ptea;////按照我们之前的理解, =是值copy,因此只要执行了ptea->addItem();m_tempvalue的值5,就会有一份copy到了lambda表达式中//因此delete pate 和 g_vec[0](10) 那个先执行无所谓,试一试Teacher140 *ptea1 = new Teacher140;ptea1->addItem();delete ptea1;cout << g_vec[0](10) << endl;//结果如下://m_tempvalue = -572662307//0//先写结论://lambda表达式的执行正确与否,取决于ptea1 对象是否存在,只有ptea1对象存在,这个lambda表达式才正确。//纠正一些概念:以及问题点分析//捕获这种概念,只针对于在创建lambda表达式的作用域内可见的非静态的局部变量,包括形参//那么m_tempvalue 符合上述定义吗?显然不符合,m_tempvalue是Teacher140的成员变量呀。//也就是说:我们实际上无法捕获到m_tempvalue的。因此也不存在值copy一份到lambda表达式的说法。//实际上 这个 等号,捕获的是this指针的值,由于有了this指针,m_tempvalue才可以访问。也就是说所有的成员变量 都是依赖于 this指针访问的,m_tempvalue //那么这样就能理通了,捕获的是this,也就是说还是依赖于 ptea1,那么将ptea1 delete了,再次执行g_vec[0](10)的时候,由于ptea1不存在了,m_tempvalue也就不存在了,问题发生}

五。静态局部变量

静态局部变量是不需要捕获的,由于在静态存储区放着,可以在lambda表达式中使用。

静态局部变量是保存在 静态存储区,它的有效期一直到程序结束。

静态局部变量的使用有点类似于按 引用 捕获这种效果。

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

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

相关文章

EXCEL中不错的xlookup函数

excel中一般要经常用vlookup函数&#xff0c;但其实经常麻烦要正序&#xff0c;从左边到右边&#xff0c;还要数列&#xff0c;挺麻烦的&#xff0c;xlookup的函数还不错&#xff0c;有个不错的一套视频介绍,B站的&#xff0c;地址是&#xff1a;XLOOKUP函数基础用法&#xff0…

rust的哈希表

新建哈希表 fn main() { use std::collections::HashMap;let mut scores HashMap::new();scores.insert(String::from("Blue"), 10);scores.insert(String::from("Yellow"), 50);println!("{:?}",scores); }访问某个元素 fn main() { use …

GB 18585-2023 壁纸中有害物质限量

壁纸/墙布因其色彩多样&#xff0c;图案丰富&#xff0c;施工方便&#xff0c;价格便宜等多种优势&#xff0c;广泛应用于室内装修材料&#xff0c;在国内&#xff0c;日本&#xff0c;欧美等地区非常普及。 GB 18585-2023壁纸中有害物质限量测试项目&#xff1a; 测试项目 测…

Eliminating Domain Bias for Federated Learning in Representation Space【文笔可参考】

文章及作者信息&#xff1a; NIPS2023 Jianqing Zhang 上海交通大学 之前中的NeurIPS23论文刚今天传到arxiv上&#xff0c;这次我把federated learning的每一轮看成是一次bi-directional knowledge transfer过程&#xff0c;提出了一种促进server和client之间bi-direction…

Day4. 文件IO的基本概念和读写

温习&#xff1a; 文件的拷贝&#xff08;单个字符&#xff09;(fgetc/fputc) #include <stdio.h>int main(void) {FILE* fp NULL;FILE* fq NULL;char ch 0;fp fopen("str.txt","r");if (fp NULL){perror("file to fopen!");retur…

网络模型及传输基本流程

1.OSI 七层模型 OSI &#xff08; Open System Interconnection &#xff0c;开放系统互连&#xff09;七层网络模型称为开放式系统互联参考模型&#xff0c;是一个逻辑上的定义和规范; 把网络从逻辑上分为了 7 层 . 每一层都有相关、相对应的物理设备&#xff0c;比如路由器…

RCS系统之:冲突解决

在RCS系统中&#xff0c;避免碰撞是至关重要的。以下是一些常见的方法和技术用于避免碰撞&#xff1a; 障碍物检测&#xff1a;机器人可以配备各种传感器&#xff0c;如激光雷达、超声波传感器、摄像头等&#xff0c;用于检测周围的障碍物和环境。通过实时监测周围情况&#xf…

插值与拟合算法介绍

在数据处理和科学计算领域,插值与拟合是两种极为重要的数据分析方法。它们被广泛应用于信号处理、图像处理、机器学习、金融分析等多个领域,对于理解和预测数据趋势具有至关重要的作用。本文将深入浅出地介绍这两种算法的基本原理,并结合C语言编程环境探讨如何在CSDN开发者社…

力扣:139. 单词拆分

动态规划&#xff1a; 1.先声明dp数组的含义为下标i表示的是在s变量中i前面的字符串是否在wordDict变量中存在&#xff0c;初始化dp【0】来进行后面dp数组的递推。同时要判断截取的值是否在wirdDict中是否存在&#xff0c;还要判断dp【j】的下标的j前面的字符串是否也在wirdDi…

数组常见算法代码总结

一、数组排序【冒泡排序】&#xff08;优化&#xff09; 1.基本实现思路: 数组排序是通过冒泡排序算法实现的&#xff0c;基本实现思路是比较相邻的元素&#xff0c;把小的元素往前移动或把大的元素往后移动&#xff0c;相邻元素两两进行比较&#xff0c;若大元素在小元素前面…

RocketMQ订阅关系不一致和不能消费时如何排查?

订阅关系不一致 调整任意一个实例的订阅关系和另一个保持一致 消费者不能消费消息 它是最常见的问题之一&#xff0c;也是每个消息队列服务都会遇到的问题 1.确认哪个消息未消费。在这时消费者至少需要手机消息id、消息key、消息发送时间段三者之一 2.确认消息是否发送成功…

HashMap使用静态初始化块添加元素

使用静态初始化块&#xff08;Static Initialization Block&#xff09; 示例代码&#xff1a; import java.util.HashMap; import java.util.Map;public class Main {public static void main(String[] args) {// 使用静态初始化块批量添加元素Map<String, Integer> h…

JVM--- 垃圾收集器详细整理

目录 一、垃圾收集需要考虑的三个事情&#xff1a; 二、垃圾回收针对的区域 三、如何判断对象已死 1.引用计数算法&#xff1a; 2.可达性分析算法 四、引用 五、生存还是死亡&#xff1f; 六、回收方法区 七、垃圾收集算法 1.分代收集理论 2.标记-清除算法 3.标记-复制算…

huggingface库LocalTokenNotFoundError:需要提供token

今天刚开始学习huggingface&#xff0c;跑示例的时候出了不少错&#xff0c;在此记录一下&#xff1a; (gpu) F:\transformer\transformers\examples\pytorch\image-classification>.\run.bat Traceback (most recent call last):File "F:\transformer\transformers\e…

一站式安装对应显卡版本的cuda和torch(windows)

前言 一年前&#xff0c;安装过cuda&#xff0c;觉得并不难&#xff0c;就没有记录。 这次安装还算顺利&#xff0c;就是在找资料的时候&#xff0c;浪费了不少时间 这次就记录下来&#xff0c;方便以后再次安装 总结安装程序&#xff1a; 1、安装python环境 2、安装VS的C环境&…

【机构vip教程】Unittest(1):unittest单元测试框架简介

unittest单元测试框架简介 unittest是python内置的单元测试框架&#xff0c;具备编写用例、组 织用例、执行用例、功能&#xff0c;可以结合selenium进行UI自动化测 试&#xff0c;也可以结合appium、requests等模块做其它自动化测试 官方文档&#xff1a;https://docs.pytho…

机试指南:3-4章

文章目录 第3章 排序与查找(一) 排序1.sort函数&#xff1a;sort(first,last,comp)2.自定义比较规则3.C函数重载&#xff1a;同一个函数名&#xff0c;有不同的参数列表4.机试考试最重要的事情&#xff1a;能把你曾经做过的题目&#xff0c;满分地做出来5.例题例题1&#xff1a…

xtu oj 1162 奇偶校验

题目描述 奇偶校验是一种在通讯中经常使用的&#xff0c;用来确认传输的字节是否正确的手段。 对于一个BYTE(8BIT),我们使用0~6bit来存储数据&#xff0c;称为数据位&#xff0c;第7位存储奇偶校验位。 如果数据位有偶数个1&#xff0c;那么第7位为0&#xff0c;否则为1。现给…

markdown绘制流程图相关代码片段记录

有时候会使用typora来绘制一些流程图&#xff0c;进行编码之类的工作&#xff0c;在网络搜集了一些笔记&#xff0c;做个记录&#xff0c;方便日后进行复习&#xff0c;相关的记录如下&#xff1a; 每次作图时&#xff0c;代码以「graph <布局方向>」开头&#xff0c;如…

kettle--JavaScript脚本日期使用

输入日期为20240216&#xff0c;运行如下代码&#xff0c;结果为true var reportdate parent_job.getVariable("v_reportdate"); var date_type parent_job.getVariable("v_date_type"); var reportdate_freportdate.substr(0,4) "/" report…