C++11 lambda表达式

8 篇文章 0 订阅

介绍lambda

一个lambda表达式表示一个可调用的代码单元。可以将其理解为一个未命名的内联函数。与任何函数类似,一个lambda具有一个返回类型、一个参数列表和一个函数体。但与普通函数不同,lambda可能定义在函数内部。一个lambda表达式具有如下形式:
[capture list] (parameter list) -> return type { function body }

  • capture list(捕获列表):lambda表达式通过捕获列表控制能够访问的外部变量
  • parameter list(参数列表)(选填):参数列表可以为空,如有传入参数,在C++11中必须说明参数的类型,
  • return type(返回类型)(选填):必须使用尾置返回来指定返回类型,如果忽略返回类型,将通过return语句推断返回类型
  • function body(函数体)
    可以忽略参数列表和返回类型,但必须包含捕获列表和函数体。如
auto f = [] { return 1; };//定义了一个可调用对象f,不接受参数,返回1

使用调用运算符就可以调用lambda:

cout << f() << endl;//打印1

优点

使用lambda表达式可以减少命名函数或者函数对象的创建,尤其是某个函数可能只会被使用一次时。需要查看函数定义时,也避免了可能的跳转,提供了更佳的可读性,使得开发人员能更加集中精力在当前代码段。
例如,希望使用sort将一个容器中的元素按长度排列。不使用lambda表达式,就需要定义一个命名函数:

bool isShorter(const string& s1,const string& s2)
{
	return s1.size() < s2.size();
}
{
	vector<string> vs{ "adsds","bcd","def","ghijdsd","klm","nodsdsa" };
	sort(vs.begin(), vs.end(),isShorter);
}

使用lambda表达式,代码就可以修改成如下:

vector<string> vs{ "adsds","bcd","def","ghijdsd","klm","nodsdsa" };
sort(vs.begin(), vs.end(),
	[](const string& s1,const string& s2)
		{return s1.size()<s2.size(); });

lambda捕获

类似参数传递,变量的捕获方式也可以是值或引用。

  • 值捕获

与传值参数类似,值捕获的前提是变量可以拷贝。与参数不同,被捕获的变量值在lambda创建时拷贝,而不是调用时:

void fun1()
{
	int a = 0;
	auto f1 = [a] { return a; };//按值捕获a,此时a已被拷贝
	++a;
	cout << f1() << endl;//输出0;f1保存创建它时对a的拷贝
}

由于被捕获变量在lambda创建时拷贝,因此随后对其进行的修改不会影响到lambda内部对应的值。

  • 引用捕获

定义lambda时可以采用引用方式捕获变量:

void fun2()
{
	int a = 0;
	auto f2 = [&a] { return a; };//按引用捕获a,f包含a的引用
	++a;
	cout << f2() << endl;//输出1;f2保存a的引用,而非拷贝
}

捕获列表中的&指出应当以引用方式捕获a。以引用方式捕获的变量与任何类型的引用的行为类似。当在lambda函数体内使用该变量时,实际是使用引用所绑定对象。在上述例子中,lambda返回a时,实际返回的是a指向的对象的值。

引用捕获与返回引用有着相同的问题和限制。在采用引用捕获时,必须保证被引用的对象在lambda执行的时候是存在的。lambda捕获的是局部变量,这些变量在函数结束后不复存在。如果lambda可能在函数结束后执行,捕获的引用指向的局部变量已经消失。

  • 隐式捕获

除了显示列出我们希望使用的变量之外,还可以让编译器根据lambda中的代码来推断使用哪些变量。为指示编译器推断捕获列表,应在捕获列表中写一个&或=。&告诉编译器采用引用方式捕获,=则表示采用值捕获。例如,可以修改上面的函数

int a = 0;
auto f2 = [=] { return a; };//隐式捕获

如果希望对部分变量值捕获,对其他变量引用捕获,可以混合使用隐式捕获和显式捕获。当混合使用隐式捕获和显式捕获时,显示捕获的变量必须使用与隐式捕获不同的方式。即,如果隐式捕获是引用方式(使用&),则显式捕获必须采用值方式。反之亦然。

lambda捕获列表

[]空捕获列表。lambda不能使用所在函数中的变量。
[=]隐式捕获列表,按值捕获。lambda将拷贝所使用的来自所在函数的实体的值
[&]隐式捕获列表,按引用捕获。lambda所使用的来自所在函数的实体都采用引用方式捕获
[names]names 是一个逗号分隔的名字列表,这些名字都是lambda所在函数的局部变量。默认情况下,变量按值捕获,如果名字前使用了&,则采用按引用捕获
[&,identifier_list]identifier_list 是一个逗号分隔的名字列表,包含0个或多个来自所在函数的变量。这些变量按值捕获。而任何隐式捕获的变量都按引用方式捕获。identifier_list 名字前不能有&
[=,identifier_list]identifier_list 中的变量都按引用捕获,而任何隐式捕获的变量都按值捕获。identifier_list 中的名字不能包括this,且名字前必须有&

可变lambda

默认情况下,对于一个值被拷贝的变量,lambda不会改变其值。如果我们希望能改变一个被捕获的变量的值,就必须在参数列表首加上关键字mutable:

void fun3()
{
	int a = 0;
	auto f3 = [a] () mutable { return ++a; };
	a = 10;
	auto j = f3();//j为1
}

一个引用捕获的变量是否可以修改依赖于此引用指向的是一个const类型还是非const类型:

void fun4()
{
	int a = 0;
	auto f3 = [&a] () { return ++a; };
	a = 10;
	auto j = f3();//j为11
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值