Lambda表达式简介
Lambda有很多叫法,有Lambda表达式、Lambda函数、匿名函数。Lambda表达式是现代C++在C ++ 11和更高版本中的一个新的语法糖。
lambda表达式(也称为lambda函数)是在调用或作为函数参数传递的位置处定义匿名函数对象的便捷方法。通常,lambda用于封装传递给算法或异步方法的几行代码。
Lambda表达式语法形式
[外部变量捕获列表] (表达式参数列表) 表达式修饰符 ->返回值类型 {表达式体};
Lambda表达式应用示例
//计算容器vec中大于3的元素个数
int test(void)
{
vector<int> vec={1,2,3,4,5,6};
int CountNum = count_if(vec.begin(),vec.end(),[](int val){return val>3;}); //传入的是lambda表达式,返回的是bool类型,其中val为遍历vec容器传入的元素值
}
PS:可以给Lambda表达式起个名字
虽然lambda表达式是匿名函数,但是实际上也可以给lambda表达式指定一个名称,如下表示:
auto f = [](int x ){return x % 3 ==0;};
此后再需要使用该lambda表达式,就可以使用f()来代替。举一个例子:
#include <iostream>
using namespace std;
int main()
{
int a = 5,b = 6;
auto f = [=]{return a+b;};//[=]按值捕获了a和b
cout << f() << endl;
return 0;
}
Lambda表达式的优缺点
优点:
- 可以直接在需要调用函数的位置定义短小精悍的函数,而不需要预先定义好函数
std::find_if(v.begin(), v.end(), [](int& item){return item > 2});
- 使用Lamdba表达式变得更加紧凑,结构层次更加明显、代码可读性更好
- Lamdba表达式语法比较灵活,增加了阅读代码的难度
- 对于函数复用无能为力
Lambda表达式语法形式详解
[外部变量捕获列表] (表达式参数列表) 表达式修饰符 ->返回值类型 {表达式体};
外部参数捕获列表
捕获列表能够捕捉上文中的局部变量以供Lambda函数在函数体中使用,当然,只能使用到定义该Lambda表达式为止定义过的局部变量。
基本语法:
[ ]:代表不捕获Lambda表达式外的变量;
&:代表以引用传递的方式捕获Lambda表达式外的变量;
=:代表以值传递的方式捕获Lambda表达式外的局部变量,即以const引用的方式传值;this:表示Lambda表达式可以使用Lambda表达式所在类的成员变量;
基本语法应用:
[a]或[=a]:表示以值传递的方式传递变量a ,即const int a,在函数体内不可改变a的值;但是可以对Lambda表达式使用mutable修饰符修饰,使得函数对象参数可以进行赋值,但是该函数对象参数不是被修改为引用传递方式。
[&a]:表示以引用传递的方式传递变量a,在函数体内可以改变a的值;
[x,&y]:x为值传递方式,y为引用传值方式;
[=,&x,&y]:除x,y为引用传递方式以外,其他参数都为值传递方式进行传递;
[&,x,y]:除x,y为值传递方式以外,其他参数都为引用传递方式进行传递;注意点:
不过值得注意的是,捕捉列表不允许变量重复捕获。下面一些例子就是典型的重复,会导致编译时期的错误。例如:
[=,a]
这里已经以值传递方式捕捉了所有变量,但是重复捕捉a
了,会报错的;[&,&this]
这里&
已经以引用传递方式捕捉了所有变量,再捕捉this
也是一种重复。
下面针对 捕获列表 的使用举一些实际例子:
[=]举例:
[=]
表示值传递方式捕获所有父作用域的变量(包括this
)int index = 1; int num = 100; auto function = ([=]{ std::cout << "index: "<< index << ", " << "num: "<< num << std::endl; } ); function();
[a]举例:
int num = 100; auto function = ([num]{ std::cout << num << std::endl; } ); function();
[&]举例:
[&]
表示引用传递方式捕捉所有父作用域的变量(包括this
)int index = 1; int num = 100; auto function = ([&]{ num = 1000; index = 2; std::cout << "index: "<< index << ", " << "num: "<< num << std::endl; } ); function();
[this]举例:
[this]
表示值传递方式捕捉当前的this指针
#include <iostream> using namespace std; class Lambda { public: void sayHello() { std::cout << "Hello" << std::endl; }; void lambda() { auto function = [this]{ this->sayHello(); }; function(); } }; int main() { Lambda demo; demo.lambda(); }
参数列表
与普通函数的参数列表基本一样,如果不需要参数传递,则可以连同括号“()”一起省略。
auto function = [] (int first, int second){
return first + second;
};
function(100, 200);
auto f = [] {
cout << "Hello" << '\n';
};
表达式修饰符
这一部分是可以省略的,常见的修饰符有如下两个:
mutable
默认情况下Lambda函数总是一个const
函数,mutable
修饰符可以取消其常量性。在使用该修饰符时,参数列表不可省略(即使参数为空)。
#include <iostream>
using namespace std;
int main()
{
int m = 0;
int n = 0;
[&, n] (int a) mutable { m = ++n + a; }(4);//n可正常被修改
cout << m << endl << n << endl;
}
异常说明 throw()
你可以使用 throw()
异常规范来指示 Lambda 表达式不会引发任何异常。与普通函数一样,如果 Lambda 表达式声明 C4297 异常规范且 Lambda 体引发异常,Visual C++ 编译器将生成警告 throw()
。
int main() // C4297 expected
{
[]() throw() { throw 5; }();
}
在MSDN的异常规范中,明确指出异常规范是在 C++11 中弃用的 C++ 语言功能。因此这里不建议大家使用。
返回值类型
Lambda表达式的返回类型会自动推导,除非你指定了返回类型,否则不必使用关键字,这部分常常省略(连同符号”->”一起省略)。
auto x1 = [](int i){ return i; };
表达式体
内容与普通函数一样,区别是除了可以使用参数之外,还可以使用所有捕获的变量。
总结一下,表达式体作用域内一共可以访问:
- 捕获变量
- 形参变量
- 局部声明的变量
- 类数据成员,当在类内声明
this
并被捕获时- 具有静态存储持续时间的任何变量,例如全局变量
#include <iostream>
using namespace std;
int main()
{
int m = 0;
int n = 0;
[&, n] (int a) mutable { m = ++n + a; }(4);
cout << m << endl << n << endl;
}
Lambda表达式工作原理
编译器会把一个Lambda表达式生成一个仿函数。
回顾,大家在使用 c++ sort算法的时候,参3会要求传递一个谓词。这个谓词可以是个仿函数,也可以是个Lambda表达式。也可以看出,二者本质是很接近的。所以,Lamdba表达式可以在STL算法库中经常使用。
示例:
auto print = []{cout << "Hello World!" << endl; };
编译器会把上面这一句翻译为下面的代码:
class print_class
{
public:
void operator()(void) const
{
cout << "Hello World!" << endl;
}
};
// 用构造的类创建对象,print此时就是一个函数对象
auto print = print_class();
不了解 仿函数 的小伙伴可顺便参考:c++仿函数及在STL中的应用-CSDN博客
参考
c++ lambda 看这篇就够了!(有点详细)_c++ 运行时 构建 lamda-CSDN博客