目录
1.简介
Lambda表达式是C++11标准引入的一种特征,它提供了一种方便的方式来定义匿名函数。Lambda表达式能够捕捉外部变量并使用它们的函数对象。
labmbda表达式由捕获列表、参数列表、返回类型和函数体组成,其中,参数列表和返回类型可以忽略,但不可以忽略捕获列表和函数体。
[捕获列表](参数列表)->返回类型{函数体}
例如: auto f = []{return 1 + 2;}
auto f = [](int x, int y)->int{ return x + y;}
2.Lambda表达式语义分析
2.1 基本语法
[捕获列表](参数列表)->返回类型{函数体}
auto add = [](int a, int b) -> int {
return a +b;
}
上述代码表示[]捕获列表为空,参数为int a和int b,返回值类型为int,函数体为return a + b的一个匿名函数,一般情况下,编译器可以自动推断出lambda表达式的返回类型,所以我们也可以不指定返回类型。
auto add = [](int a, int b) {
return a +b;
}
2.2 捕获列表
如果需要在匿名函数内部使用所在函数体内的变量而不想传参,可以使用捕获列表来实现
int c = 12;
int d = 30;
auto add = [c, d](int a, int b) -> int{
cout << "d = " << d << endl;
return c;
}
如果在匿名函数内部加入对变量c,d修改的操作,则无法编译通过,因为上述捕获列表是通过值进行传递的,无法进行修改,如果想要对c,d进行修改,需要传入引用:
int c = 12;
int d = 30;
auto add = [&c, &d](int a, int b) -> int{
cout << "d = " << d << endl;
return c;
}
下表是捕获列表的分类
[] | 空捕获列表,Lambda不使用所在函数中的变量 |
[names] | names是一个逗号分隔的名字列表,这些名字都是Lambda所在函数中的变量,例如[a,b,...]。默认情况下,这些变量按值传递,名字前面如果加上了&,则按引用传递。 |
[&] | 隐式捕获列表,lambda函数体内可以使用lambda所在作用范围内所有可见的局部变量(包括lambda所在类的this),并且是引用传递方式 |
[=] | 隐式捕获列表,lambda函数体内可以lambda所在作用范围内所有可见的局部变量(包括lambda所在类的this),并且是值传递方式。 |
[this] | lambda函数体内可以使用lambda所在类中的成员变量 |
[a] | 将a按值进行传递,函数体内不能修改传递进来的a的拷贝。 |
[&a] | 将a按引用进行传递 |
[a, &b] | 将a按值进行传递,b按引用进行传递 |
[=, &a, &b] | 除a和b按引用进行传递,其他参数都按值进行传递 |
[&, a, b] | 除a和b按值进行传递,其他参数都按引用进行传递 |
3.作用
通过引入lambda表达式,使得编写匿名函数变得更加简洁和方便,提高了代码的可读性和灵活性。
4.使用注意事项
4.1 生命周期问题
lambda表达式捕获外部变量时需要注意外部变量的生命周期,以避免访问已经销毁的资源。
示例1 正确示例
该示例能够正常运行,Lambda表达式使用了值捕获[=],这意味着Lambda表达式会复制捕获的变量的值,而不是引用它们。这样,即使捕获的变量超出作用域,Lambda表达式中仍然有它们的副本,因此Lambda表达式可以正常访问和使用这些变量的值
示例2 错误示例
这段代码虽然能够正常运行,但是从理论上来说是存在错误的,在test()中定义lambda表达式时捕获的是test()中所有的变量,并按引用的方式进行传递,test()返回之后其变量被销毁,那么fun函数中持有的引用将会是无效的引用,这可能会导致未定义的行为,因此,按引用的方式进行捕获时一定需要注意变量的生命周期,如果在lambda中不需要对捕获变量进行修改可以考虑按值的方式进行捕获。
看看chatgpt是怎么看待这段代码的
示例3 错误示例
这个错误就不用多说了,销毁一个空间之后还继续使用。