Lambda表达式

Lambda表达式是C++11引入的一种功能,用于方便地定义匿名函数。它可以捕获外部变量,支持按值或按引用捕获,并且可以指定返回类型或由编译器推断。Lambda函数的语法包括捕获列表、参数列表、返回类型和函数体,可以用于简化代码和提高效率。
摘要由CSDN通过智能技术生成

Lambda表达式

lambda表达式(也称为lambda函数)是在调用或作为函数参数传递的位置处定义匿名函数对象的便捷方法。通常,lambda用于封装传递给算法或异步方法的几行代码 。

Lambda表达式

[外部变量访问方式说明符] (参数表) -> 返回值类型
{
    语句块
}

其中,“外部变量访问方式说明符”可以是=&,表示{}中用到的、定义在{}外面的变量在{}中是否允许被改变。=表示不允许,&表示允许。当然,在{}中也可以不使用定义在外面的变量。“-> 返回值类型”可以省略。

[ capture list ] ( parameter list) -> return type { function body }

capture list,捕获列表,是一个lambda所在函数中定义的局部变量的列表。lambda函数体中可以使用这些局部变量。捕获可以分为按值捕获和按引用捕获。非局部变量,如静态变量、全局变量等可以不经捕获,直接使用。

捕捉列表总是作为lambda的开始,即出现于lambda的开始处。它是lambda的引出符(即开始标志)。编译器可以根据该“标志”来作出判断出该函数是否为lambda函数。同时“捕捉列表”能够捕捉上下文中的变量以作为lambda函数使用。

parameter list,参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略。所传参数和捕捉参数不一样,不自带const,可以修改从C++14开始,支持默认参数,并且参数列表中如果使用auto的话,该lambda称为泛化lambda(generic lambda)。

mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空时,也要带上小括号)。mutable 只是让传值捕捉变量const属性去掉了,但是捕捉的a,b仍是拷贝,外部的a,b无法被修改,所以mutable 很少用,意义不大

return type,返回类型,这里使用了返回值类型尾序语法(trailing return type synax)。可以省略,这种情况下根据lambda函数体中的return语句推断出返回类型,就像普通函数使用decltype(auto)推导返回值类型一样;如果函数体中没有return,则返回类型为void。

function body,与任何普通函数一样,表示函数体。
在这里插入图片描述
注意:在lambda函数定义中,参数列表和返回值类型都是可省略部分,而捕捉列表和函数体可以为空,但不可省略,因此C++11中最简单的lambda函数为:[]{}; 该lambda函数不能做任何事情。

auto f = []{return 42;}
cout << f() << endl;

上面的lambda表达式,定义了一个可调用对象f,它不接受参数,返回42。Lambda的调用方式与普通函数的调用方式相同。

int main()
{
      int a = 0, b = 1;
      auto Add1 = [](int x, int y)->int{return x+y;}; //完整写法
      auto Add2 = [](int x, int y){return x+y;}; //省略写法

      std::cout << Add1(a,b) << endl;
      std::cout << Add2(a,b) << endl;

      return 0;
}

在这里插入图片描述

捕获列表

[]:什么也不捕获
[=]:捕获所有一切变量,都按值捕获
[&]:捕获所有一切变量,都按引用捕获
[=, &a]:捕获所有一切变量,除了a按引用捕获,其余都按值捕获
[&, =a]:捕获所有一切变量,除了a按值捕获,其余都按引用捕获
[a]:只按值捕获a
[&a]:只按引用捕获a
[a, &b]:按值捕获a,按引用捕获b
[this] 表示值传递方式捕捉当前的this指针

[=]拷贝捕获
表示以值传递的方式捕获所有捕获范围内的对象(隐式捕获)

#include <iostream>

using namespace std;

int main()
{
	int i = 1024;
	auto func = [=] {	// [=] 表明将外部的所有变量拷贝一份到该函数内部
		cout << i << endl;
	};
	/* 错误写法
	auto func2 = [] { //未指名捕获, i 不存在  运行会报错
		cout << i << endl;
	};
	*/
	func();
	return 0;
}

在这里插入图片描述
[&]引用捕获
表示以地址传递的方式捕获所有捕获范围内的对象(隐式捕获)

#include <iostream>

using namespace std;

int main()
{
	int i = 1024;
	cout << &i << endl;
	auto pfun = [&] {   //
		i = 1023;
		cout << i << endl;
		cout << &i << endl;
	};
	pfun();
	return 0;
}

在这里插入图片描述
[&a]只按引用捕获a
按引用捕获可以让lambda表达式能够访问引用变量,这种捕获方式在编译期间不会把变量的值复制到lambda的局部作用域,而是直接对这个引用所指的变量进行操作。

#include <iostream>

using namespace std;

int main() {
	int v1 = 1;
	int v3 = 3;
	cout << &v1 << endl;
	auto f = [&v1] { 
		cout << &v1 << endl;
		cout << v1 << endl; 
	};
	f();
	v1 = 2;
	cout << v3 << endl;
	return 0;
}

在这里插入图片描述
因为这是按引用捕获,lambda在访问时也将直接访问这个引用,也就是函数内的&v1,因此即使在lambda被创建之后v1的值有所改变,lambda也依旧是可以直接访问这个引用变量。

#include <iostream>

using namespace std;

int main() {
	int v1 = 1;
	int v3 = 3;
	auto f = [&v1] { cout << v1 << endl; };
	v1 = 2;
	f();
	cout << v3 << endl;
	return 0;
}

在这里插入图片描述
值得一提的是,按引用捕获在实际应用中是有风险的,主要原因是lambda表达式在被创建后到被执行前这段期间里,这个引用有可能会变得不复存在了,这时候就很难保证这个引用变量在执行的时候一定存在。所以除非我们能确保它在lambda执行的时候是一定存在的,否则应该尽量避免用按引用捕获。

既然按引用捕获有风险,那它有没有存在的必要呢?也有,比如当我们想捕获的局部作用域变量是一个不可复制的变量的时候,就只能通过引用的方式来捕获了。以下的例子中,我们想让lambda可以访问ostream对象,但是它是不可复制的,于是我们将其引用给lambda:

#include <iostream>

using namespace std;

int main() {
	ostream& os = cout;
	auto f = [&os] { os << "hello"; };
	f();
	os << endl;
	return 0;
}

在这里插入图片描述
但如果我们尝试将os按值捕获,则在编译时报错:
在这里插入图片描述
[this] 捕获 this 指针
可以使用this类型的成员变量和成员函数。如下所示,在test内的lambda捕获this后,可以使用class A的公有成员函数print和私有成员函数x。

#include <iostream>
class A
{
public:
	void print() {
		std::cout << "class A\n";
	}
	void test()
	{
		auto fun = [this] {
			print(); //也可以this->print()
			x = 5;   //也可以this->x
			std::cout << x << std::endl;
		};
		fun();
	}
private:
	int x;
};

int main()
{
	A t;
	t.test();
	return 0;
}

在这里插入图片描述

lambda 表达式与function

lambda表达式的参数列表,即(…)部分。和我们使用普通函数的参数列表一样。

#include <iostream>
using namespace std;

int main()
{
	int x = 3;
	auto pfun = [](int x) {
		cout << "x:" << x << endl;
	};
	pfun(x);
	return 0;
}

在这里插入图片描述
捕获列表在lambda表达式定义时即发生捕获;而参数列表是在调用lambda表达式时才会传参。

#include <iostream>
using namespace std;

int main(int argc, char* argv[])
{
	int val = 1;

	//定义lambda表达式
	auto fun = [val](int c)
	{
		cout << "inner:" << val << " " << c << endl;
	};
	val = 11; //不会改变inner的val的值,因为在此之前已经捕获完成, 是值传递
	fun(2); //调用
	system("pause");
	return 0;
}

在这里插入图片描述

mutable

mutable用来表示捕获列表中的对象可被改写。如果一个匿名函数声明为mutable,则在函数体中可以改写传值方式捕获的变量(传址方式捕获的变量不受mutable限制,是可以修改的。)

如果传值进匿名函数的变量只能使用,而不能修改。如下语句会报编译错误

#include <iostream>
using namespace std;

int main(int argc, char* argv[])
{
	int val = 1;

	//定义lambda表达式
	auto fun = [val](int c)
	{
		val = 12;
		cout << "inner:" << val << " " << c << endl;
	};
	val = 11; //不会改变inner的val的值,因为在此之前已经捕获完成, 是值传递
	fun(2); //调用
	system("pause");
	return 0;
}

在这里插入图片描述
但是用mutable声明过的匿名函数可以修改捕获的变量。如下所示:

#include <iostream>
using namespace std;

int main(int argc, char* argv[])
{
	int val = 1;

	//定义lambda表达式
	auto fun = [val](int c)mutable
	{
		val = 12;
		cout << "inner:" << val << " " << c << endl;
	};
	val = 11; //不会改变inner的val的值,因为在此之前已经捕获完成, 是值传递
	fun(2); //调用
	system("pause");
	return 0;
}

在这里插入图片描述

retType

对于返回值的使同普通函数,如下示例代码

#include <iostream>
using namespace std;

int main(int argc, char* argv[])
{
	//定义lambda表达式
	auto fun = []()->string
	{
		string str = "hello world";
		return str;
	};

	cout << fun() << endl;
	
	return 0;
}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阳光开朗男孩

你的鼓励是我最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值