lambda历史悠久,在数理逻辑和计算机科学领域,lambda被用来表示一种匿名函数这种匿名函数代表了一种λ演算(lambda calculus),但是在c++领域直到c++11才引入lambda表达式,本文先打算从lambda函数入手、后续会继续从lambda与仿函数、lambda基础应用、lambda的实验以及lambda与STL关系等5个方面来阐述一下c++11新特性lambda表达式
一、lambda函数
先看一个例子来直观地感受一下lambda函数
#include<iostream>
using namespace std;
int main()
{
int salary = 8000,expense = 5000;
auto surplus = [](int salary,int expense)->int{return salary-expense;};
cout<<surplus(salary,expense)<<endl;
return 0;
}
功能:lambda函数接收两个参数(int,int),并返回两者之差。
结构:与普通函数相比没有函数名,取而代之的是一对方括号,此外返回值是采用返回类型追踪的方式声明。
lambda函数的语法定义如下:
-
[capture](paramters) mutable->return-type{statement}
-
[capture}:捕捉列表。[]是lambda引出符。编译器根据该引出符判断接下来的代码是否是lambda函数。捕捉列表能够捕捉上下文的变量供lambda函数使用。
-
[parameters]:参数列表,与普通函数参数列表一样
-
mutable:mutable修饰符。默认情况下lambda函数总是一个const函数,mutable可以取消其常量性。在使用该修饰符的时候参数列表不可省略(即使参数为空)
-
->retrurn-type:返回值类型,不需要返回值的地方可以连同->一起省略,在返回值类型明确的情况下,也可以省略该部分,让编译器自己推导。
-
{statement}:函数体。内容和普通函数一样,不过除了可以使用参数之外还可以使用所有捕获的变量。
所以最简单的lambda表达式可以写成 []{} ,当然此函数啥都干不了。。。
对于捕捉列表的使用是lambda表达式与普通函数最大的区别之一,就是lambda函数可以通过捕捉列表访问一些上下文的数据。具体地,捕捉列表描述了上下文中哪些的数据可以被lambda使用,以及使用方法(值传递、引用传递),上个例子中我们使用了参数的方法传递变量,现在我们使用捕捉变量来改写这个例子。
#include<iostream>
using namespace std;
int main()
{
int salary = 8000,expense = 5000;
auto surplus = [salary,expense]()->int{return salary-expense;}; //salary和expense被lambda函数捕获,参数列表就可以省略
cout<<surplus()<<endl;
return 0;
}
此时salary和expense可以视为函数的一种初始状态,lambda函数则是基于初始状态进行的运算,这与函数简单基于参数的运算是不同的。
当然了,捕获和参数列表可以同时存在,即捕获又传参
#include<iostream>
using namespace std;
int main()
{
int salary = 8000,expense = 5000,tmp = 1;
auto surplus = [salary,expense](int tmp )->int{return salary-expense;};
cout<<surplus(tmp )<<endl;
return 0;
}
**语法上,捕捉列表由多个捕捉项组成,并以逗号分隔。捕捉列表主要有如下几种形式:
- [var]表示值传递方式捕获变量var //如 auto surplus = salary,expense->int{return
salary-expense;}; - [=]表示值传递方式捕捉所有父定义域的变量(包括this) //如 [=]{return a+b;};
- [&var]表示引用传递捕获变量var //如 [&a]{return a+b;};
- [&]表示引用传递捕获父作用域 //如 [&]{return a+b;};
- [this]表示值传递方式捕获当前的this指针 //如 [this]{int a = this->…};**
但是要注意想要在mutbale在引用传递和值传递上的区别
#include<iostream>
using namespace std;
int main()
{
int salary = 8000,expense = 5000;
auto surplus = [=](int salary,int expense) ->int{salary = 10000; return salary-expense;};
cout<<"salary:"<<salary<<endl;
cout<<"surplus:"<<surplus(salary,expense)<<endl;
return 0;
}
运行结果如下:
salary:8000
surplus:5000
这个例子有一定迷惑性,也是博主随手写成这样了,才发现的问题,因为是值传递,其实[=]值传递过去的父空间的salary和expense没有在lambda表达式内用到,而采用就近原则用的是参数列表(int salary,int expense)在下面调用的时候的参数传递的值。
言归正传,看一下mutable
#include<iostream>
using namespace std;
int main()
{
int salary = 8000,expense = 5000;
auto surplus = [=]() ->int{salary = 10000; return salary-expense;};
cout<<"salary:"<<salary<<endl;
cout<<"surplus:"<<surplus()<<endl;
return 0;
}
编译报错如下:
因为lambda函数默认是const函数,所以要修改需要加上mutable(可修改的),如下:
#include<iostream>
using namespace std;
int main()
{
int salary = 8000, expense = 5000;
auto surplus = [=]() mutable ->int {salary = 10000; return salary - expense; };
cout << "salary:" << salary << endl;
cout << "surplus:" << surplus() << endl;
return 0;
}
运行结果:
再来看一下引用传递有啥区别
#include<iostream>
using namespace std;
int main()
{
int salary = 8000, expense = 5000;
auto surplus = [&]() ->int {salary = 10000; return salary - expense; };
cout << "salary:" << salary << endl;
cout << "surplus:" << surplus() << endl;
return 0;
}
运行结果:
结果看出引用传递是否加mutable都是可以修改的,但是有个需要注意的地方,按照一般函数的理解,引用是要被修改了的,但是这边的salary还是一开始的8000,而不是10000,所以说明不论是值传递还是引用传递,传递进去的值对父空间原值无影响。&与mutable的测试就不在赘述了
注意点:
[=,&] 这种写法‘=’已经以值传递的方法捕捉过所有的父作用域的变量,&再去捕捉的时候就会报错
人,总是要有一点精神的,不是吗