1.概述
lambda表达式实际上是一种就地定义和使用的匿名的可调用类型。类似一种语法糖的存在。
2.lambda形式
[capture] (parameters) mutable -> return-type{ statement };
2.1.capture
捕获列表的形式
a. [var
],以值传递方式捕获上一层作用域的自动变量var
。
b. [=
],以值传递方式捕获上一层作用域的全体自动变量(成员函数使用时包括this
)。
c. [&var
],以引用方式捕获上一层作用域的自动变量var
。
d. [&
],以引用方式捕获上一层作用域的全体自动变量(成员函数使用时包括this
)。
e. [this
],以值传递方式捕获上一层作用域的自动变量this
(必须位于成员函数内)。
可以对上述形式进行组合,如[=, &a, &b
],表示以引用方式捕获上一层作用域的自动变量a,b
。以值传递方式捕获上一层作用域的其余自动变量。
注意点:
a. 何为自动变量
{}
作用域内的局部变量。
int c = 1;
static d = 1;
int fun(int a)
{
int b;
return a+b;
}
上述a,b位于fun作用域内是自动变量。c,d位于全局作用域不是自动变量。
b. 捕获列表里只允许捕获上一层作用域的自动变量
c. 理解捕获的意义,理解值捕获,引用捕获差异,理解对上一层之上作用域内变量的使用
#include <iostream>
int a = 1;
static int b = 1;
int fun(int c){
int d = 1;
int dd = 11;
auto f = [d, &c]()->int{
int n1 = c + d;
int n2 = a + b /*+ dd*/;
printf("a_%d,b_%d,c_%d,d_%d,n1_%d,n2_%d\n", a,b,c,d,n1, n2);
};
f();
c = 2;
d = 2;
a = 11;
f();
return 0;
}
int main(){
fun(11);
return 0;
}
c.1.捕获的意义是针对上一层作用域内的自动变量只有先被捕获,然后才可在lambda作用域内去使用。上一层之上作用内的变量无需捕获就能使用。
c.2.值捕获和引用捕获差异在将lambda
转化为等价的可调用类型后则很好理解
#include <iostream>
int a = 1;
static int b = 1;
int fun(int c){
int d = 1;
int dd = 11;
auto f = [d, &c]()->int{
int n1 = c + d;
int n2 = a + b /*+ dd*/;
printf("a_%d,b_%d,c_%d,d_%d,n1_%d,n2_%d\n", a,b,c,d,n1, n2);
c = 0;
};
class None{
public:
None(int d, int& c):m_d(d), m_c(c){
}
int operator()() const{
int n1 = m_c + m_d;
int n2 = a + b /*+ dd*/;
printf("a_%d,b_%d,c_%d,d_%d,n1_%d,n2_%d\n", a,b,m_c,m_d,n1, n2);
m_c = 0;
}
private:
int m_d;
int& m_c;
};
None none(d, c);
f();
printf("c_now_%d\n", c);
none();
printf("c_now_%d\n", c);
c = 2;
d = 2;
a = 11;
f();
printf("c_now_%d\n", c);
none();
printf("c_now_%d\n", c);
return 0;
}
int main(){
fun(11);
return 0;
}
上述定义了和lambda等价的可调用类型。易于观察到值捕获,引用捕获的差异。
c.3.理解lambda中对上一层之上作用域内变量的使用
参考c.2中等价的可调用类型,lambda产生了一个匿名内部可调用类型。内部类型可直接使用上一层之上作用域内的变量,且这类变量不像捕获的上一层作用域的自动变量,不会成为可调用类型的成员变量。
c.4.理解lambda
定义,通过lambda
对象执行调用两处时间点。
参考c.2
中等价的可调用类型,lambda
定义处不仅定义了一个匿名内部可调用类型,还实例化了一个该类型的对象实例。通过捕获列表完成了实例的构造。通过lambda
对象执行调用只是在通过实例对象执行其operator()
操作符调用。
2.2.parameters
这部分等价于可调用类型中重载的operator()
操作符的形参列表。
值得注意的是当形参列表为空时,(parameters)
部分可省略。
2.3.mutable
借助c.2中等价的可调用类型很好理解。lambda
对应的可调用类型里operator()
操作符是const
修饰的,这意味者无法在lambda的作用域中修改捕获的变量。添加mutable
修饰后,则可以。
值得注意的是:
a. 只有需要在lambda
作用域内修改捕获的自动变量时,才需要添加mutable
修饰。
b. 对于引用捕获的自动变量。引用本身是常量,但引用指向的底层对象并非常量。所以,作用域中通过引用捕获的变量执行赋值是允许的。参考2.1
部分的c.2
中lambda
作用域内执行c=0;
2.4.-> return-type
这部分等价于可调用类型中重载的operator()
操作符的返回类型。
值得注意的是:
a. 这里采用了后置返回类型
b. -> return-type
部分可省略,省略时,从lambda
作用域内return
语句自动推断返回类型。
2.5.{ statement }
这部分等价于可调用类型中重载的operator()
操作符的{ statement }
。