C++14学习笔记(3)——Lambda捕获表达式

学习笔记(1)提到了C++14对Lambda表达式参数推断的扩展。在C++14中,Lambda表达式还有一个重要的扩展,就是这篇文章中将要提到的捕获表达式。


在C++11中,捕获列表(就是方括号[]中的东西)用于指出哪些外层变量可以在Lambda内访问,并且指出访问的方式是值传递还是引用传递。而在C++14中,我们还可以对捕获列表的捕获变量“赋值”。这就带来了一些比较奇怪的特性,比如下面的代码:

//代码3.1
#include 
#include 
using namespace std;

int main()
{
	int a = 2;
	[a = sin(a)]()
	{
		cout << cos(a) << endl;
	}();
	cout << a << endl;
	cout << cos(a) << endl;
	return 0;
}
猜猜这段代码会输出什么结果?


这段代码最难理解的地方是捕获列表(方括号)中的a = sin(a),因为这个表达式和通常的赋值表达式含义完全不同。

通常,我们写下a = sin(a);时,意味着首先用a当前的值计算出sin(a),然后把a的值替换为刚刚计算出的sin(a)。

然而在捕获列表中的a = sin(a),这两个a不是同一个a。sin(a)中的a是对外层类型为int的a的捕获,因而这个a的类型为int,值为2;而赋值运算符=左边的a是一个新定义的变量,这个变量的作用域是Lambda体,类型由=右边的表达式推断出来为double,值等于=右边的值sin(2)。

因此,在lambda表达式中的cout语句输出的cos(a),根据作用域规则,这个a是在捕获列表中定义的类型为double,值为sin(2)的a,因而输出的结果为cos(sin(2)),即0.6143;而Lambda外的输出语句不受Lambda的影响,a是int类型,值为2。

于是在本例中出现了一种很神奇的情况:在同一个表达式中,同一个标识符指代了不同的变量。

代码3.1的输出结果为:

0.6143
2
-0.416147


C++14中的这个新特性允许了在捕获列表中定义前面没有出现过的变量,但必须赋予一个值,并且不使用类型说明符和auto,类型由编译器自动推断:

//代码3.2
#include 
#include 
using namespace std;

int main()
{
	int a = 2;
	[b = tan(a)]()	//b是新定义的变量,类型自动推断为double,值为tan(2)
	{
		cout << b << endl;
	}();
	return 0;
}
输出结果为:-2.18504


编译器根据捕获列表中的变量是否被赋值区分捕获变量和新定义的变量:

//代码3.3
int a = 2;
auto l1 = [a]()		//捕获外层的a,类型为int,值为2
{
	cout << a << endl;
};
auto l2 = [a = 3.14]()	//新定义的a,类型为double,值为3.14
{
	cout << a << endl;
};

网上的资料说这个特性可以支持不可拷贝的变量的捕获,本人也亲自试验过。我觉得这个特性的意义在于当某个变量需要经过某个函数(例如std::move)“处理”后,才能被捕获时,提供一种“沿用”原来变量名的方法。

你可能感兴趣的:(编程语言)