lamda表达式可以看作是一个匿名函数。编译器在编译的时候,会将lamda表达式处理成一个仿函数类,类名是类名是不重复的随机名称(因为一个作用域中可能存在多个仿函数),返回该仿函数的对象。
lamda既然可以看作是一个匿名函数,那就至少具备除函数名以外的两部分:返回值、形参列表、函数体
lamda表达式的书写格式:
[capture-list](parameters)mutable->return-type{ statement }
/*
[](const int x, const int y)mutable->int{
return x + y;
}
*/
目录
一、lamda表达式各部分说明
1、capture-list(必要部分)
2、parameters(可选部分)
3、mutable(可选部分)
4、->return-type(可选部分)
5、 statement(必要部分)
二、lamda表达式的底层处理
一、lamda表达式各部分说明
1、capture-list(必要部分)
(1) 捕获方式
capture-list表示捕获列表,lamda表达式可以通过捕获列表来获取上下文的变量,以供lamda表达式使用,无需通过形参列表传递变量。
捕获列表有两种方式来捕获变量:值传递、引用传递
捕获方式 | 解释 |
[var] | 以值传递的方式捕获变量 var,如[ a ],捕获上下文中的变量a |
[=] | 以值传递的方式捕获父作用域中的所有变量(包括this) |
[&var] | 以引用传递的方式捕获变量var |
[&] | 以引用传递的方式捕获父作用域中的所有变量(包括this) |
[this] | 以值传递的方式捕获当前的this指针 |
(2) 注意事项
不能以相同的方式捕捉同一个变量。比如[=, a], = 表示以传值的方式捕捉所有的变量(包括变量a),a表示以传值的方式捕捉变量a,这个时候变量a以传值的方式被捕捉了两次
[=, a](){} // 错误,重复捕获
不能捕捉父作用域之外的变量(包括全局变量)
int a = 10;
auto obj1 = []{}; // 注意,此时lamda表达式没有父作用域,捕获列表必须为空
int main()
{
auto obj2 = [a]{}; // 错误,不可以捕获全局变量
}
lamda表达式不能相互赋值。lamda表达式底层是被处理成一个类,不同的lamda表达式相当于不同的类(即便是类型相同也不行),不同的类之间不能相互赋值。
int main() {
auto obj1 = [] {};
auto obj2 = [] {};
obj2 = obj1; // 错误,obj1和obj2底层被处理成不同的类,他们的类名不可能重复
}
2、parameters(可选部分)
parameters表示调用lamda表达式时,要传递给lamda表达式的值。如果无需传递参数,该部分可以省略。
[]mutable->int{ return 0; };
3、mutable(可选部分)
默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表parameters不可省略(即使参数为空)
[]()->int{ return 0; };
4、->return-type(可选部分)
return-type表示lamda表达式的返回值类型。无返回值时,可以省略;有返回值时,也可以省略,此时交由编译器推导。
[](){ return 0; };
5、 statement(必要部分)
statement表示函数体,在函数体中,可以使用形参列表,也可以使用所有捕获到的变量,函数体中的语句可以有多条。
int main()
{
int a = 10;
auto obj = [&a](const int x){
int c = x - a;
return c;
};
return 0;
}
二、lamda表达式的底层处理
以下面这个lamda表达式为例
int main() {
auto obj1 = [] {};
obj1();
cout << typeid(obj1).name() << endl;
}
由此可见,lamda表达式实际上是被处理成一个类,类名是 lamda_<uuid> ,这样保证了每个lamda表达式的唯一性。lamda表达式的返回值就是该类的对象。
我们通过反汇编来看一下lamda表达式的调用过程,我们会发现,类lamda_<uuid> 中重载了operator(),这样就可以使用对象来调用lamda表达式了。