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

一、捕获列表中的 &

& : 捕获外部作用域中的所有变量,并作为引用在lambda表达式中使用。按照引用这种捕获方式,会导致lambda表达式包含绑定到局部变量的引用。

#include <iostream>
#include <future>
#include <vector>
#include <map>
#include <functional>
#include <string>
#include <algorithm>
#include <ctime>


using namespace std;

std::vector<std::function<bool(int)>> gv;

void myfunc()
{
	srand((unsigned)time(NULL));
	int tmpvalue = rand() % 6;
	gv.push_back(
		[&](int tv) {
			if (tv % tmpvalue == 0) {
				return true;
			}
			return false;
		}
	);
}

int main()
{
	myfunc();
	cout << gv[0](10) << endl; //非法调用,程序将会出现不可预料的结果。
	//1.用 & 去捕获 tmpvalue 的值,当myfunc()执行完之后,tmpvalue作为在stack上的局部变量,内存已经被操作系统回收了。
    //2.这个时候tmpvalue 已经是一个无效的值了,程序将会出现不可预料的结果。

	return 0;
}

二、形参列表可以使用auto

C++14允许在lambda表达式的形参列表中使用auto。

#include <iostream>
#include <future>
#include <vector>
#include <map>
#include <functional>
#include <string>
#include <algorithm>
#include <ctime>


using namespace std;

std::vector<std::function<bool(int)>> gv;

void myfunc()
{
	srand((unsigned)time(NULL));
	int tmpvalue = rand() % 6;
	gv.push_back(
		//[&](auto tv){
		[=](auto tv) {
			if (tv % tmpvalue == 0) {
				return true;
			}
			return false;
		}
	);
}

int main()
{
	myfunc();
	cout << gv[0](10) << endl; //非法调用,程序将会出现不可预料的结果。
	//1.用 & 去捕获 tmpvalue 的值,当myfunc()执行完之后,tmpvalue作为在stack上的局部变量,内存已经被操作系统回收了。
    //2.这个时候tmpvalue 已经是一个无效的值了,程序将会出现不可预料的结果。
	//3.引用捕获方式超出范围的情形也叫做“引用悬空”,采用按照值捕获的方式可以解决眼下这个问题。因为按值捕获是赋值了一份作为副本。


	return 0;
}

三、成员的捕获问题

#include <iostream>
#include <future>
#include <vector>
#include <map>
#include <functional>
#include <string>
#include <algorithm>
#include <ctime>


using namespace std;

std::vector<std::function<bool(int)>> gv;


class AT
{
public:
	int m_tmpvalue = 7;
	void addItem()
	{
		gv.push_back(
			[=](auto tv) { // = 等价于 this 捕获
				cout << "m_tmpvalue : " << m_tmpvalue << endl;
				if (tv % m_tmpvalue == 0) {
					return true;
				}
				return false;
			}
		);
	}
};

//分析成员变量的捕获方式。
//1.= 是按照值捕获的意思。
//2.我们会认为这个[=] 是按照值捕获,是使得我们能够访问成员变量 m_tmpvalue。
//3.所以我们顺理成章的认为,这里的这个lambda表达式是所使用的 m_tmpvalue 是按照值捕获的。
//4.如果按照上述所想的话,我们在 cout << gv[0](10) << endl; 之前 delete pat;也不会出现什么问题,因为按照值捕获是复制了一份副本出来。
//5.实验结果表明:lambda表达式的执行正确与否,取决于pat对象是否存在,只有pat对象存在,这个lambda表达式执行才正确。
//6.结论:捕获这个概念,只针对于在创建lamdba表达式的作用域内可见的 非静态 局部变量(包括形参)。m_tmpvalue 并不是非静态局部变量,m_tmpvalue是一个
//成员变量,成员变量是不能被捕获到的。这里的 = 本质上是 this 捕获,m_tmpvalue 本质上是等价于 this->m_tmpvalue 

int main()
{
	AT* pat = new AT();

	pat->addItem();

	delete pat;

	cout << gv[0](10) << endl;

	

	return 0;
}

修改后就可以正常捕获了:

#include <iostream>
#include <future>
#include <vector>
#include <map>
#include <functional>
#include <string>
#include <algorithm>
#include <ctime>


using namespace std;

std::vector<std::function<bool(int)>> gv;


class AT
{
public:
	int m_tmpvalue = 7;
	void addItem()
	{
		auto tmpvalueCopy = m_tmpvalue;

		gv.push_back(
			[tmpvalueCopy](auto tv) { // = 等价于 this 捕获
				cout << "m_tmpvalue : " << tmpvalueCopy << endl;
				if (tv % tmpvalueCopy == 0) {
					return true;
				}
				return false;
			}
		);
	}
};

int main()
{
	AT* pat = new AT();

	pat->m_tmpvalue = 70; //后面捕获到的是这个值。

	pat->addItem();

	//pat->m_tmpvalue = 90; //放在后面没有用,因为lambda 表达式已经捕获70作为副本了。

	delete pat;

	cout << gv[0](10) << endl;

	

	return 0;
}

四、广义lambda 捕获 :generalized lambda capture

C++14 引入的广义的lambda捕获

#include <iostream>
#include <future>
#include <vector>
#include <map>
#include <functional>
#include <string>
#include <algorithm>
#include <ctime>


using namespace std;

std::vector<std::function<bool(int)>> gv;


class AT
{
public:
	int m_tmpvalue = 7;
	void addItem()
	{
		gv.push_back(
			[tmpvalueCopy = m_tmpvalue](auto tv) { // 将m_tmpvalue 复制到闭包里面来
				cout << "m_tmpvalue : " << tmpvalueCopy << endl; //使用的是副本
				if (tv % tmpvalueCopy == 0) {
					return true;
				}
				return false;
			}
		);
	}
};

int main()
{
	AT* pat = new AT();

	pat->m_tmpvalue = 70; //后面捕获到的是这个值。

	pat->addItem();

	//pat->m_tmpvalue = 90; //放在后面没有用,因为lambda 表达式已经捕获70作为副本了。

	delete pat;

	cout << gv[0](10) << endl;

	

	return 0;
}

五、静态局部变量

1.捕获是不包含静态局部变量的,也就是说,静态局部变量是不能被捕获的。

2.但是可以在lambda表达式中使用的,另外,静态局部变量是保存在静态存储区的,它的有效期一直到程序结束。

3.但是static变量和 按值捕获(=)不一样,它类似于按引用捕获(&)。

#include <iostream>
#include <future>
#include <vector>
#include <map>
#include <functional>
#include <string>
#include <algorithm>
#include <ctime>


using namespace std;

std::vector<std::function<bool(int)>> gv;

void myfunc()
{
	int tmpvalue = 4;
	gv.push_back(
		[=](int tv) {
			cout << "tmpvalue :" << tmpvalue << endl;
			if (tv % tmpvalue == 0) {
				return true;
			}
			return false;
		}
	);
	tmpvalue++;
}

int main()
{
	myfunc();
	cout << gv[0](10) << endl;
	myfunc();
	cout << gv[1](10) << endl;

	return 0;
}
#include <iostream>
#include <future>
#include <vector>
#include <map>
#include <functional>
#include <string>
#include <algorithm>
#include <ctime>


using namespace std;

std::vector<std::function<bool(int)>> gv;

void myfunc()
{
	static int tmpvalue = 4;
	gv.push_back(
		[](int tv) {
			cout << "tmpvalue :" << tmpvalue << endl;
			if (tv % tmpvalue == 0) {
				return true;
			}
			return false;
		}
	);
	tmpvalue++;
}

int main()
{
	myfunc();
	cout << gv[0](10) << endl; 
	myfunc();
	cout << gv[1](10) << endl;

	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

repinkply

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值