更多c++后台开发相关知识,可以看我的博客:https://blog.csdn.net/ypshowm
一个lambda表达式表示一个可调用的代码单元。与函数一样,也具有一个返回类型,一个参数列表,和有一个函数实体。
表达式如下:
[capture list] (params list) mutable exception-> return type { function body }
各项具体含义如下
- capture list:捕获外部变量列表(捕获的是lambda所在函数中定义的局部变量的列表,通常为空)
- params list:形参列表
- mutable指示符:用来说用是否可以修改捕获的变量
- exception:异常设定
- return type:返回类型(lambda必须使用尾置类型返回)
- function body:函数体。
我们可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体。
如下几种缩写:
1 | [capture list] (params list) -> return type {function body} |
2 | [capture list] (params list) {function body} |
3 | [capture list] {function body} |
lambda与普通函数的不同:
lambda不能有默认单数,一个lambda调用的实参数目永远与形参数目相等。
一个lambda只能在其捕获列表中捕获一个它所在函数中的局部变量,才能在函数体中使用该变量。
捕获列表只用于局部非static变量。
值捕获
与传值参数类似,采用值捕获的前提是变量可以拷贝。与参数不同,被捕获的变量的值是在lambda创建时拷贝,而不是调用时拷贝:
void fcn()
{
size_t v1 = 42; //局部变量
//将v1拷贝到名为f的可调用对象
auto f = [v1] {return v1;};
v1 = 0;
auto j = f(); //j的值为42,;f保存了我们创建它时v1的拷贝,也就是42
}
由于被捕获的变量的值是在lambda时创建拷贝,因此随后对其修改不回影响到lambda内对象的值。
引用捕获:
void fcn()
{
size_t v1 = 42; //局部变量
//对象f2包含v1的引用
auto f2 = [&v1] {return v1;};
v1 = 0;
auto j = f2(); //j的值为0,f2保存v1的引用
}
注意,由于不能对返回局部变量的引用。因此,采用引用方式捕获一个变量,就必须确保被引用的对象在lambda执行的时候是存在的lambda捕获的都是局部变量,这些变量在函数结束以后就不复存在了。。(当以引用的方式捕获一个变量是,必须保证在lambda执行时变量是存在的)。
隐私捕获:在捕获列表中写一个&或者=
&告诉编译器采用引用捕获方式
=告诉编译器采用值捕获方式。
混用隐私捕获和显示捕获时,隐私捕获必须放在前面,即捕获列表中的第一个元素必须是一个&或=。同时,显示捕获与隐私捕获的方式必须不同。即如果隐私捕获时引用的方式(使用了&),那么显示捕获命名变量名必须采用值捕获。如果隐私捕获采用了值方式,那么显示捕获命名变量必须采用引用方式。
捕获形式 | 说明 |
---|---|
[] | 不捕获任何外部变量 |
[变量名, …] | 默认以值得形式捕获指定的多个外部变量(用逗号分隔),如果引用捕获,需要显示声明(使用&说明符) |
[this] | 以值的形式捕获this指针 |
[=] | 以值的形式捕获所有外部变量 |
[&] | 以引用形式捕获所有外部变量 |
[=, &x] | 变量x以引用形式捕获,其余变量以传值形式捕获 |
[&, x] | 变量x以值的形式捕获,其余变量以引用形式捕获 |
可变lambda
默认情况下,采用值捕获的变量,lambda是不会修改其值的。如果我们希望一个被捕获的变量的值,就必须在参数列表首加上mutable关键字。可变lambda能省略参数列表:
void fcn3()
{
size_t v1 = 42; //局部变量
//f可以改变它所捕获的变量的值
auto f = [v1] () mutable { return ++v1; };
v1 = 0;
auto j = f(); //j为43
}
当然,若捕获的是一个非const类型,也可以用引用来修改其值
void fcn4()
{
size_t v1 = 42; //局部变量
//v1 是一个非const变量
//可以通过f2中的引用来改变它
auto f2 = [&v1] { return ++v1; };
v1 = 0;
auto j = f(); //j为1
}
指定lambda返回类型:
如果lambda的函数体包含任何单一return语句之外的内容,且没有指定返回类型,则返回void。此时就可能出错。如:
[] (int i ) { if(i < 0) return -1; else return i; };
编译器会推断其返回的是void,但是他返回的是int。这个时候,就必须要用到尾置类型的返回:
[] (int i ) -> int { if(i < 0) return -1; else return i; };