一.概念
Lambda表达式是现代C++在C++11和更高版本中的一个新的语法糖。
- lambda表达式(也称为lambda函数)是在调用或作为函数参数传递的位置处定义匿名函数对象的便捷方法。通常,lambda用于封装传递给算法或异步方法的几行代码。
- Lambda有很多叫法,有Lambda表达式、Lambda函数、匿名函数,本文中为了方便表述统一用Lambda表达式进行叙述。
ISOC++标准官网展示了一个简单的lambda表示式实例:
cpp
#include <algorithm>
#include <cmath>
void abssort(float* x,unsigned n)
{
std::sort(x, x + n,
// Lambda expression begins
[](float a, float b) {
return (std::abs(a) < std::abs(b));
}// end of lambda expression
};
}
示例中第三个参数是一个传递一排序规则的函数,但是这个示例中直接将排序函数的实现写在应该传递函数的位置,省去了定义函数的过程。对于这种不需要复用,且短小的函数,直接传递函数体可以增加代码的可读性。
lambda表达式是一种匿名表达式,一般格式定义为:

- 捕获列表。在C++规范中也称为Lambda导入器,捕获列表总是出现在Lambda函数的开始处。实际上,[是Lambda引出符。编译器根据该引出符判断接下来的代码是否是Lambda函数,捕获列表能够捕捉上下文中的变量以供Lambda函数使用。
- 参数列表。与普通函数的参数列表一致。如果不需要参数传递,则可以连同括号"("一起省略。
- 可变规格。mutab1e修饰符,
默认情况下Lambda函数总是一个const函数,mutable可以取消其常量性。
在使用该修饰符时,参数列表不可省略(即使参数为空)。 - 异常说明。用于Lamdba表达式内部函数抛出异常。
- 返回类型。追踪返回类型形式声明函数的返回类型。我们可以在不需要返回值的时候也可以连同符号"->"一起省略。此外,在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行推导。
- lambda函数体。内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕获的变量。
二.lambda函数捕捉列表
Lambda表达式与普通函数最大的区别是,除了可以使用参数以外,Lambda函数还可以通过捕获列表访问一些上下文中的数据。具体地,捕捉列表描述了上下文中哪些数据可以被Lambda使用,以及使用方式(以值传递的方式或引用传递的方式)。
语法上,在"[]"包括起来的是捕获列表,捕获列表由多个捕获项组成,并以逗号分隔。捕获列表有以下几种形式:
-
\]表示不捕捉任何变量
auto function = ( []{
std::cout<<"Hello World"<<std::endl;
}
};
function();
* \[value\]表示捕捉变量value
```cpp
int num =100;
auto function = ( [num]{
std::cout<<num<<std::endl;
}
};
function();
-
=\]表示以值传递的方式捕捉所有父作用域的变量(包括this)
int num =100;
int index=1;
auto function = ( [=]{
std::cout<<num<<index<<std::endl;
}
};
function();
* \[\&value\]表示引用传递捕捉变量value
```cpp
int num =100;
auto function = ( [&value]{
num=1000;
std::cout<<num<<std::endl;
}
};
function();
-
\&\]表示引用传递捕捉所有父作用域的变量(包括this)
int index=1;
int num=100;
auto functiion=([&]{
num=1000;
index=2;
std::cout<<num<<" "<<index<<std::endl;
)
};
function();
* \[this\]表示捕捉当前的this指针
```cpp
#include<iostream>
using namespace std;
class Lanbda{
public:
void sayHello(){
cout<<"Hello"<<endl;
}
void lambda(){
auto function=[this]{
this->sayHello();
};
function();
}
};
int main()
{
Lambda lam;
lam.lambda();
return 0;
}
-
=,\&\]拷贝与引用混合 \[=,\&a,\&b\]表示以引用传递的方式捕捉变量a和b,以值传递的方式捕捉其他所有父类变量。
三.参数列表
除了捕获列表之外,Lambda还可以接受输入参数。参数列表是可选的,并且在大多数方面类似于函数的参数列表。
cpp
auto function = [](int a,int b){
return a+b;
}
function(100,200);
四.可变规则mutable
mutable修饰符,默认情况下Lambda函数总是一个const函数,mutable可以取消其常量性。在使用该修饰符时,参数列表不可省略(即使参数为空)。
cpp
#include<iostream>
using namespace std;
int main()
{
int m=0;
int n=0;
[&,n](int a)mutable {m=++n+a;}(4);
cout<<m<<endl;
return 0;
}
五.异常说明
不建议使用。
六.返回类型
Lambda表达式的返回类型会自动推导。除非你指定了返回类型,否则不必使用关键字。
返回型类似于通常的方法或函数的返回型部分。但是,返回类型必须在参数列表之后,并且必须在返回类型->之前包含类型关键字。如果Lambda主体仅包含一个return语句或该表达式未返回值,则可以省略Lambda表达式的return-type部分。
如果Lambda主体包含一个return语句,则编译器将从return表达式的类型中推断出return类型。否则,编译器将返回类型推导为void。
七.函数体
和正常函数体一样。
八.lambda函数陷阱
1.按值捕获&捕获时机
cpp
int main()
{
string str = "abc";
//1ambda表达式用的功能是,使用str给传递进来的参数赋值
auto localstr = [=](string &s)
{s = str;};
str = "he11o world";
string str1;localstr(str1);
cout << "strl=" << str1 << end1;
return 0;
}
结果是str1=abc。
当闭包生成的那一刻,被捕获的变量已经按值赋值的方式进行了捕获,后面那个str再怎么变化,已经和闭包对象里面的值没有关系了.localStr中str中的值在localStr 定义的时候就已经确定为abc了,不会再发生变化。
2.按引用捕获&悬空引用
悬空引用就是说我们创建了一个对象的引用类型的变量,但是被引用的对象被析构了、无效了。
cpp
#include <iostream>
#include <string>
#include <functional>
using namespace std;
//std::function是一个可调用对象包装器,是一个类模板,可以容纳除了类成员函数指针之外的所有可调用对象
//它可以用统一的方式处理函数、函数对象、函数指针,并允许保存和延迟它们的执行。
std::function<void(void)>Func;
void test()
{
string str = "abc";
func = [&]()
{ string a = str; cout << a <<endl;};
}
int main()
{
test();
func();
return 0;
}
结果会报错
原因:func引用的对象str在离开作用域test()后被析构了,在main函数中执行func()导致找到引用的对象str,导致出错;