1、lambda基本概念 ———《C++ Primer 5th》
一个 lambda 表达式表示一个可调用的代码单元。我们可以将其理解为一个未命名的内联函数。与任何函数类似,一个 lambda 具有一个返回类型、一个参数列表和一个函数体。但与函数不同,lambda 可能定义在函数内部。一个lambda表达式具有如下形式
[capture list] (parameter list) -> return type {function body}
其中,capture lis(捕获列表)是一个lambda所在函数中定义的局部变量的列表(通常为空);return type、parameter list 和 function body与任何普通函数一样,分别表示返回类型、参数列表和函数体。但是与普通函数不同,lambda必须使用尾置返回来制定返回类型。
我们可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体
auto f = [] {return 42;}; //此例中,我们定义了一个可调用对象f,它不接受参数,返回42
lambda的调用方式与普通函数的调用方式相同,都是使用调用运算符:
cout << f() <<endl; //打印42
在lambda中忽略括号和参数列表等价于制定一个空参数列表。此例中,当调用 f 时,参数列表是空的。如果忽略返回类型,lambda 根据函数体中的代码推断出返回类型。
如果函数体只是一个reutrn 语句,则返回类型从返回的表达式类型推断而来。否则,返回类型为void
2、捕获列表
虽然lambda可以出现在一个函数中,但不能想当然认为他就可以使用所在函数的变量。例:
int n = 3;
auto f = [] {
cout << n << endl; //错误,n 未定义
}
必须使用捕获列表捕获 n 才能使用(如需捕获多个变量,用逗号‘,’隔开)
int n = 3;
int m = 4;
auto f = [n,m] {
cout << n << endl;
cout << m << endl;
};
f();
3、值捕获
被捕获的变量的值实在lambda创建时拷贝,而不是调用时拷贝
int n = 3;
auto f = [n] {
cout << n << endl;
};
n++;
f();
输出结果为3 创建时n为3 随后对其修改不会影响到lambda内对应的值
4、引用捕获
int n = 3;
auto f = [&n] {
cout << n << endl;
};
n++;
f();
输出结果为4
5、隐式捕获
可以通过在捕获列表中写一个 & 或者 = 让编译器根据lambda体重的代码来推断我们要使用哪些变量。 & 告诉编译器采用引用捕获方式,= 表示采用值捕获方式
int n = 3;
auto f = [=] {
cout << n << endl;
};
n++;
f();
结果为3
int n = 3;
auto f = [&] {
cout << n << endl;
};
n++;
f();
结果为4
6、混合隐式捕获和显式捕获
当我莪们混合使用隐式捕获和显式捕获时,捕获列表中的第一个元素必须是一个 & 或者 = 。此符号指定了默认捕获方式为引用或值。
显式捕获的变量必须使用与隐式捕获不同的方式
int n = 3;
int m = 4;
auto f = [&,m] {
cout << "n=" << n << ends;
cout << "m=" << m << endl;
};
n++;
m++;
f();
输出结果为 n=4 m=4; n为引用传递,m为值传递
7、可变lambda
默认情况下,对于一个值被拷贝的变量,lambda不会改变其值。如果希望能改变一个被捕获的变量的值,就必须在参数列表尾部加上关键字 mutable 。因此,可变lambda 不能省略参数列表
int m = 4;
auto f = [m]()mutable{
cout << "m=" << ++m << endl;
};
f();
输出结果为 m=5 如果没有mutable 第三行将会报错。 没有参数列表() 单独加mutable 也是不可以的
引用捕获的变量可否修改依赖于此引用指向的是一个const类型还是一个非const类型
int n = 4;
const int &m = n;
int &m2 = n;
auto f = [&]{
cout << "m=" << ++m << endl; //报错
cout << "m2=" << ++m2 << endl; //正确
};
f();