C++11新增了很多特性,Lambda表达式(Lambda expression)就是其中之一,很多语言都提供了 Lambda 表达式,如 Python,Java ,C#等。本质上, Lambda 表达式是一个可调用的代码单元[1]^{[1]}[1]。实际上是一个闭包(closure),类似于一个匿名函数,拥有捕获所在作用域中变量的能力,能够将函数做为对象一样使用,通常用来实现回调函数、代理等功能。Lambda表达式是函数式编程的基础,C++11引入了Lambda则弥补了C++在函数式编程方面的空缺。
Lambda 表达式就是一个可调用的代码单元,我们可以将其理解为一个未命名的内联函数。与任何函数类似,一个Lambda具有一个返回类型、一个参数列表和一个函数体。但与函数不同,Lambda可以定义在函数内部,其语法格式如下:
[capture list](parameter list) mutable(可选) 异常属性->return type{function body}
capture list(捕获列表)
是一个Lambda所在函数中定义的局部变量的列表,通常为空,表示Lambda不使用它所在函数中的任何局部变量。也可以使用 “=” ,表示函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(编译器自动为我们按值传递了所有局部变量)也可以使用 “this” , [this] 截取当前类中的this指针。如果已经使用了&或者=就默认添加此选项。也可以使用 “&”,表示 以引用方式捕获外部作用域中所有变量
捕获列表
parameter list (参数列表) 相当于函数入参
mutable 参数选项
ret 返回值类型
body 函数体
其中Lambda表达式必须的部分只有capture list和function body。
- // 表示:我们定义一个 可调用的对象 f ,它不接受任何参数,返回值是42
- auto f=[]{return 42;}
- main.cpp
-
- #include
- #include
- #include
- auto f = [] {return 42; };
- int a = 43;
-
- auto lamdba = [=]()->void {
-
- std::cout << "in lamdba: " << a << std::endl;
- };
-
- int main() {
-
- // 直接调用 Lamdba几种方式(1)
- std::cout << f() << std::endl;
- std::cout << [] {return 42; }() << std::endl;
-
- // 直接调用 Lamdba几种方式(2)
- lamdba();
-
- // 直接调用 Lamdba几种方式(3)有参
- // [](int a)-> void {
- // cout << a;
- // }(1);
-
- // 外部间接调用: Lambda函数变量 Vector
- [](int val) {
- cout << val;
- }
- //上面Lamdba表达式 是由 for_each() 调用的
-
- for_each(v.begin(),v.end(),[](int val)
- {
- cout << val;
- });
- }
添加 mutable 选项修改变量值
- #include
- auto f = [] {return 42; };
- int a = 43;
-
- auto lamdba = [=]()->void {
-
- std::cout << "in lamdba: " << a << std::endl;
- };
-
-
- auto lamdba2 = [=]() mutable ->void {
- a = 45;
- std::cout << "in mutable lamdba: " << a << std::endl;
- };
-
- int main() {
-
- lamdba();
- // 添加 mutable 修改变量值
- lamdba2();
- }
-
-
- // 打印结果
- in lamdba: 43
- in mutable lamdba: 45
- #include
- using namespace std;
- typedef int(*pfunc)(int x, int y);
-
- int main()
- {
- auto func = [](int x, int y)->int {
- return x + y;
- };
- pfunc p1 = nullptr;
- p1 = func; //lambda表达式向函数指针转换
-
- std::cout << p1(1, 2) << std::endl;
-
- return 0;
- }
-
- // 打印结果 :3
C++11中,默认情况下lambda函数是一个const函数,按照规则,一个const成员函数是不能在函数体内改变非静态成员变量的值。但是我一定要修改,那又怎么办了 ?(下面第二、第三、第四点均可以实现)
(1)可以看到在const的lambda函数中无法修改按值捕捉到的变量。lambda函数是通过仿函数来实现的,捕捉到的变量相当于是仿函数类中的成员变量,而lambda函数相当于是成员函数,const成员函数自然不能修改普通成员变量;(不能修改捕获的值)
(2)使用引用的方式捕获的变量在常量成员函数中值被更改则不会导致错误,其原因简单地说,由于const_ref_lambda 不会改变引用本身,而只会改变引用的值,所以编译通过;(通过捕获值得引用,改变引用的值,不修改值本身)
(3)使用mutable修饰的mutable_val_lambda,去除了const属性,所以可以修改按值方式捕获到的变量;(去除const属性,就可以修改值)
(4)按值传递参数的const_param_lambda修改的是传入lambda函数的实参,当然不会有问题。(修改参数,当然可以该值)
- #include
- using namespace std;
-
- int main()
- {
- int val = 0;
- // auto const_val_lambda = [=] { val = 3; }; // 编译失败,不能在const的lambda函数中修改按值捕获的变量val
-
- auto mutable_val_lambda = [=]() mutable { val = 3; };
- printf("mutable_val_lambda:%d", mutable_val_lambda);
-
-
- auto const_ref_lambda = [&]()-> int {
- val + 3;
- };
- printf("\nconst_ref_lambda:%d", const_ref_lambda);
-
- auto const_param_lambda = [](int v) { v = 4; };
- const_param_lambda(val);
- printf("\nconst_param_lambda:%d", const_param_lambda);
-
- return 0;
- }
-
- 打印结果
- mutable_val_lambda:0
- const_ref_lambda:6422284
- const_param_lambda:6422389
这里提出一个疑问: 如何打印出 变量val 修改后的值
(1)Lambda函数和STL
Lambda函数的引入为STL的使用提供了极大的方便。比如下面这个例子,当你想遍历一个vector的时候,原来你得这么写:
很明显,相比于传统的for循环、函数指针和仿函数,使用lambda函数更加简洁。如果处理vector成员的业务代码更加复杂,那么更能凸显Lambda函数的便捷。而且这么写之后执行效率反而会提高,因为编译器有可能使用循环展开来加速执行过程。
- #include
- #include
- #include
- using namespace std;
-
- vector<int> v = { 1,2,3,4,5,6,7,8,9 };
-
- void printFunc(int v) {
- cout << v;
- };
-
- // 循环vector
- int main() {
- //传统的for循环
- for (auto itr = v.begin(), end = v.end(); itr != end; itr++)
- {
- cout << *itr;
- }
- cout << endl;
-
- //“函数指针”
- for_each(v.begin(), v.end(), printFunc);
-
- cout << endl;
-
- //仿函数
- struct CPrintFunc
- {
- void operator() (int val)const { cout << val; }
- };
- for_each(v.begin(), v.end(), CPrintFunc());
-
- cout << endl;
-
-
- // Lamdba 表达式
- for_each(v.begin(), v.end(), [](int val)
- {
- cout << val;
- });
- }
-