lambda()
- lambda()语法
- 捕捉列表
- mutable
- lambda 底层原理
- 函数对象与lambda表达式
lambda()语法
lambda表达式书写格式:
[capture-list] (parameters) mutable -> return-type
{
statement
}
咱们一个个来解释:
[capture-list]
:捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据 [] 来判断接下来的代码是否为lambda
函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。不能省略。
(parameters)
: 参数列表,与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略
mutable
: 默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。
-> return-type
:返回值类型,用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。
statement
:函数体,在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。不可省略。
因此,把可以省略的都省略掉,最那简单的lambda
函数是 []{}
,该 lambda 表达式没有任何意义。该lambda函数不能做任何事情。
接下来写一个lambda函数:
auto add = [](int x, int y)->int
{
return x + y;
};
lambda表达式实际上可以理解为无名函数,该函数无法直接调用,如果想要直接调用,可借助auto将其赋值给一个变量。
调用:
//第一种
add(10, 20);
//第二种
[](int x, int y)->int {return x + y;}(10, 20);
可以看出,lambda函数和普通函数在组成和调用上都很相似。参数列表,返回值,函数体都不在多叙述。
捕捉列表
捕捉列表描述了上下文中那些数据可以被lambda使用。
如:
int a = 10;
int b = 20;
auto add = [a,b]()
{
return a + b ;
};
直接捕捉了 a b 变量,且是传值捕捉,lambda函数体内的a, b变量,只是外边 a b 的一份拷贝。且默认无法修改。
要想修改,可以使用 mutable 进行修饰。
mutable
如:
auto add = [a, b]() mutable
{
a = 20;
return a + b;
};
就不会报错,但因为是传值,所以lambda
函数内部 a的变化,无法影响外部的a变量。
mutable 用的比较少。
当然,lambda函数
和普通函数一样,捕捉列表,可以传值捕捉,也可以传引用捕捉。
int x = 10;
int y = 20;
//捕捉列表
//传引用 参数列表
auto fun1 = [](int& x, int& y) {
int tmp = x;
x = y;
y = x;
};
// 传引用捕捉
auto fun2 = [&x,&y]() {
int tmp = x;
x = y;
y = x;
};
//对上下文所有变量进行传引用捕捉
auto fun3 = [&]() {
x = y;
};
//对除y以外的所有变量传引用捕捉,y传值捕捉
auto fun4 = [&, y] {
;
};
//对y进行传值捕捉,对其余变量进行传引用捕捉
auto fun5 = [=, &y] {
;
};
lambda 底层原理
看如下代码:
int main()
{
int a = 10;
int b = 20;
auto add = [a, b]() mutable
{
a = 20;
return a + b;
};
cout << typeid(add).name() << endl;
cout << sizeof(add) << endl;
return 0;
}
lambda 函数的类型变量是什么呢?
lambda 类型的大小又是多少呢?
从运行结果上来看,其大小为一,类型大致为一个类,具体是什么我们现在也不清楚。
函数对象与lambda表达式
函数对象,又称为仿函数,即可以想函数一样使用的对象,就是在类中重载了operator()运算符的类对象。
//仿函数
class math {
public:
int operator() (int x, int y)
{
return x + y;
}
};
int main()
{
int a = 10;
int b = 20;
//仿函数对象
math m;
//lambda函数
auto add = [](int a,int b)
{
return a + b;
};
m(a, b);
add(a, b);
return 0;
}
我们从反汇编上来看,
仿函数底层代码,调用了 重载的 ()
lambda()
函数的底层:
我们也可以看出,也是调用了一个lambda 类里的重载的()
,
不妨看出,lambda()函数的底层就是一个重载了()
的空类。
所以就可以知道,lambda类型的大小为1了:
因为,它的底层是一个空类,是一个仿函数。
至于它的类型,如图:
也就是,上图是 lambda_UUID
UUID
是 通用唯一识别码(Universally Unique Identifier)的缩写,是通过一种特殊的算法计算出来的具有唯一识别信息的 数据。
也就是说,每一个lambda()
对象的类型都不一样。
也就不存在不同lambda()对象相互赋值的情况。
结语
本次的博客就到这了。
我是Tom-猫,
如果觉得有帮助的话,记得
一键三连哦ヾ(≧▽≦*)o。
咱们下期再见。