参考:
C++ 11 Lambda表达式
C++之Lambda表达式
1、Lambda表达式写法格式
[捕获变量列表] (输入参数列表) mutable 或 exception 声明 -> 返回值类型 {函数体}
几种省略写法:
[] (输入参数列表)-> 返回值类型 {函数体} // 捕获变量列表为空
[] (输入参数列表){函数体} // 自动推导返回值类型,没有return语句时返回类型为void
[]{函数体} // 捕获列表、输入参数均为空
2、Lambda的名称与执行
2.1为Lambda指定名称并执行
auto lambda_name = [](int a) { std::cout << a; };
lambda_name(1); // 执行Lambda
lambda_name(2);
lambda_name 为该Lambda的名称,相当于函数名。lambda_name 的实际类型随表达式的实现而定,取决于编译器使用是, 类型来跟踪Lambda。可以像使用常规函数那样使用有名称的Lambda。
2.2定义的同时执行Lambda
也可以在定义Lambda的同时执行它:
[](int a) { std::cout << a; }(1);
后面的(1)为实参列表。此时该Lambda并没有名称。
2.3不能在直接执行时给Lambda取名
auto lambda_name = [](int a) { std::cout << a; }(1); // 编译失败,error:“lambda_name”: 变量不能具有类型“void”
因为在直接执行Lambda时,前面的lambda_name会被认为是一个变量,用于接收Lambda的返回值,而非Lambda的名称。但此处Lambda的返回类型为void,变量不能为void类型,因此报错。
如果Lambda有返回值,则可定义变量来接收返回值:
auto m = [](int a) { return ++a; }(1); // 编译通过,m=2
auto n = [](int x) { return [](int y) { return y * 2; }(x)+6; }(5); // 编译通过,n=16
3、捕获变量列表
捕获变量列表用于列写需要捕获的外部变量名称。Lambda可以捕获其可见范围内的外部变量,在捕获列表[]中列写后即可在Lambda内部使用这些变量。
捕获的方式分为两种:值捕获 和 引用捕获
3.1值捕获
int a = 1;
auto lambda_name = [a]() { std::cout << a; };
lambda_name();
直接在[]中列写需要捕获的外部变量名称,类似于函数参数的值传递。拷贝的参数在函数内部是只读的,Lambda内部不能改变捕获得到的变量值。加上mutable关键字可以改变拷贝值,但不会影响原值:
int a = 1;
// auto lambda_name = [a](){ std::cout << ++a; }; // 编译失败,不能改变值捕获的变量值
auto lambda_name = [a]()mutable { std::cout << ++a; }; //编译成功,但不改变变量a的原始值
lambda_name();
std::cout << a; // a=1
也可以用=符号值捕获所有可见范围内的变量:
{
int x = 10;
}
int a = 1;
int b = 2;
// auto lambda_name = [=]() { std::cout << x; }; // 编译失败,x不可见
auto lambda_name = [=]() { std::cout << a + b; }; // 编译成功,a、b可见
lambda_name();
3.2引用捕获
int a = 1;
auto lambda_name = [&a]() { std::cout << ++a; };
lambda_name();
std::cout << a; // a=2
在捕获的外部变量名称前面加&,类似于函数参数的引用传递。Lambda不需要mutable关键字就允许改变捕获得到的变量值,并且会改变该变量的原始值。
也可以用&符号引用捕获所有可见范围内的变量:
{
int x = 10;
}
int a = 1;
int b = 2;
// auto lambda_name = [&]() { std::cout << x; }; // 编译失败,x不可见
auto lambda_name = [&]() { std::cout << (++a) + (++b); }; // 编译成功,a、b可见
lambda_name();
std::cout << a << b;
3.3混合捕获
值捕获与引用捕获可以混合使用:
int a = 1;
int b = 2;
auto lambda_name = [&a, b]() mutable { std::cout << (++a) + (++b); }; // 显示5
lambda_name();
std::cout << a << b; // a=2,b=2
或者:
int a = 1;
int b = 2;
int c = 3;
// auto lambda_name = [&a,=]() mutable { std::cout << (++a) + (++b) + (++c); }; // 编译失败,error:"=": 捕获默认只能出现在 lambda 捕获列表的开头
// auto lambda_name = [a,&]() mutable { std::cout << (++a) + (++b) + (++c); }; // 编译失败,error:"&": 捕获默认只能出现在 lambda 捕获列表的开头
auto lambda_name = [=,&a]() mutable { std::cout << (++a) + (++b) + (++c); }; // 编译成功,显示9
lambda_name();
std::cout << a << b << c; // a=2,b=2,c=3
除了变量a为引用捕获,其他变量均为值捕获。注意:=必须写在&a的前面。
4、Lambda用于STL
在C++11中,对于接受函数指针或函数对象的函数,可以使用Lambda作为其参数。
std::vector<int> buf = {1,4,3,5,2};
std::sort(buf.begin(), buf.end(), [](const int a, const int b) {return a < b; });
for (int x : buf) std::cout << x;
Lambda在此处代替了一般使用的函数指针,使代码更加简洁。