C++ lambda 表达式

发布时间:2023年12月17日

lambda 表达式

lambda 表达式有时也称为 lambda 函数,或者直接简称为 lambda ;它是定义和使用匿名函数对象的一种简便的方式。
一条 lambda 表达式包含以下组成要件:

  • 一个可能为空的捕获列表,指明定义环境中的哪些名字能被用在 lambda 表达式内,以及这些名字的访问方式是拷贝还是引用;捕获列表位于[]内。
  • 一个可选的参数列表,指明 lambda 表达式所需的参数;参数列表位于()内。
  • 一个可选的 mutable 修饰符,指明该 lambda 表达式可能会修改它自身的状态(即,改变通过值捕获的变量的副本)。
  • 一个可选的 noexcept 修饰符。
  • 一个可选的->形式的返回类型声明。
  • 一个表达式体,指明要执行的代码,表达式体位于{}内。

即,一个 lambda 表达式具有如下形式:

		[捕获列表](参数列表)->返回值{表达式体};

在 lambda 表达式的概念中,传参、返回结果以及定义表达式体等环节都与函数的相应概念是一致的。
区别在于,函数没有提供局部变量“捕获”的功能;这意味着 lambda 表达式可以作为局部函数使用,而普通函数不能。

lambda 的类型

lambda 表达式是一种局部类类型,它含有一个构造函数以及一个 const 成员函数 operator()()。
lambda 表达式不仅可以作函数实参,还能用于初始化一个声明为 auto 或者std::function<R(AL)>的变量。其中,R 是 lambda 的返回类型,AL是它的类型参数列表。
lambda 表达式作函数实参

	vector<int> v{ 1,3,3,7,9 };
	for_each( v.begin(), v.end(), [](int& x){x += 1;} ); //每个元素都加一

lambda 表达式用于初始化一个声明为 auto 的变量

	vector<int> v{ 1,3,3,7,9 };
	auto f = [](int& x) { x += 1; };
	for_each( v.begin(), v.end(),f );

如果一个 lambda 什么也不捕获,则我们可以将它赋值给一个指向正确类型函数的指针。

	int (*p)(int) = [](int a) {return a; };
	cout << (*p)(42) << endl; //输出 42

调用与返回

除了关于捕获的规则外,lambda 的大多数规则都是从函数和类借鉴而来。
然而,有两点需要注意:

  • 如果一个 lambda 表达式不接受任何参数,则其参数列表可被忽略。
  • lambda 表达式的返回类型能由 lambda 表达式本事推断得到。
    • 如果 lambda 的主体部分不包含 return 语句,则该 lambda 的类型是 void。
    • 如果 lambda 的主体部分只包含一个 return 语句,则该 lambda 的类型是该 return 表达式的类型。
    • 其它情况下,必须显示地提供一个返回类型。
      可以忽略参数列表和返回类型,但是必须永远包含捕获列表和函数体。
	auto f = [] {return 42; }; //lambda 最简形式:[]{表达式体};

lambda 表达式的调用方式与普通函数调用的方式相同。

	auto max = [](int x, int y) { return x > y ? x : y; };
	cout << max(10,30) << endl; //输出 30

当需要为一个 lambda 表达式定义返回类型时,必须使用尾置返回类型。

	auto max = [](int x, double y) ->int
		{
			if (x > y)
				return x;
			else if (x < y)
				return y;
			else
				return 0;
		};
	cout << max(10, 30) << endl;  //输出 30

捕获

值捕获

通过在捕获列表中列出局部变量的名字,就可以在 lambda 表达式中访问它们了。
与函数传递参数类似,通过值捕获传递的是局部变量的副本;不同的是,被捕获的变量的值是在 lambda 表达式创建时拷贝,而不是在调用时拷贝。

void fun(void)
{
	int x =10, y = 20;     //局部变量
	auto max = [x,y](){ return x > y ? x : y; }; 
	x = 30; // 对 x 的修改,不会改变 max() 返回的结果
	cout << max() << endl; //输出 20
}

默认情况下,对于一个通过值捕获的变量的副本, lambda 不会改变其值。
如果希望能改变一个通过值捕获的变量的副本,就必须在参数列表后面加上关键字 mutable。

void fun(void)
{
	int x = 1, y = 2; //局部变量
	auto f = [x, y]() mutable { x += 10; y += 10; 	cout << x << ',' << y << endl; };
	cout << x << ',' << y << endl; //调用前,x = 1,y = 2
	f();                           //调用中,x = 11,y = 12
	cout << x << ',' << y << endl; //调用后,x = 1,y = 2
}

引用捕获

只有通过引用的捕获才允许修改调用环境中的变量。

void fun(void)
{
	int x = 1, y = 2; //局部变量
	auto f = [&x, &y]() { x += 10; y += 10; };
	cout << x << ',' << y << endl; //调用前,x = 1,y = 2
	f();
	cout << x << ',' << y << endl; //调用后,x = 11,y = 12
}

lambda 引入符[]的形式有很多种:

  • []空捕获列表。即,在 lambda 表达式内部无法使用其外层上下文中的任何局部名字。
  • [&]通过引用隐式捕获。所有局部名字都能使用,所有局部变量都通过引用访问。
  • [=]通过值隐式捕获。所有局部名字都能使用,所有局部名字都指向局部变量的副本。
  • [捕获列表]显示捕获。以&为前缀的局部名字通过引用捕获,其它变量通过值捕获;捕获列表中,可以出现this
  • [&,捕获列表]对于名字没有出现在捕获列表中的局部变量,通过引用隐式捕获;捕获列表中,可以出现this;列出的名字不能以&为前缀;捕获列表中的变量名通过值的方式捕获。
  • [=,捕获列表]对于名字没有出现在捕获列表中的局部变量,通过值隐式捕获;捕获列表中,不允许包含this;列出的名字必须以&为前缀;捕获列表中的变量名通过引用的方式捕获。

在捕获列表中,以&为前缀的局部名字总是通过引用捕获;相反地,不以&为前缀的局部名字总是通过值捕获。

文章来源:https://blog.csdn.net/w541541/article/details/135048513
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。