lambda表达式:
lambda是c++在c++11中引入的新特性,作为一个语法糖,lambda在c++11、c++14、c++17、c++20以及即将推出的c++23中都有更新。
lambda函数与普通函数的区别
lambda函数与普通函数的区别在于,除了使用参数表之外,lambda函数可以通过捕获列表来访问上下文的数据,在捕获列表中,捕获列表规定了上下文的哪些数据和变量是可以被我们使用的,以什么方式被使用的。语法上,lambda函数的是以*[]开头的,[]*里面的内容就是一个捕获列表。捕获列表可以由多个捕获项组成,多个捕获项之间以,隔开。接下来说说lambda的语法。
捕获列表:
[ ]表示不捕获任何变量
由于捕获列表中为空,此处我们告诉编译器说这个lambda函数不需要捕获上下文中的任何元素。
[var]表示以值传递的方式捕获变量var
其中的var是代替任意的变量名,假如存在一个变量为a,那么[a]的意思就是以值传递的方式将a传入lambda表达式中。值得一提的是,以这个方式传入lambda函数中的值是一个副本,且是const的,如果想要解除lambda表达式中的这一属性,那么就必须在后面参数表的下一个位置加上mutable声明。以下是一段示例的代码。
#include <bits/stdc++.h>
using namespace std;
int main(){
int a=100;
auto f1=[a](){return a+=100;
f1();//报错,此处的a是一个const变量,我们不能改变a的值};
auto f2=[a](){return a+100;
f2();//正确,此时函数的返回值为200};
cout<<f2();//结果为200;
return 0;
}
[&var]表示传入var的引用,
与上面相同的是,var代表的是任意的变量名,但与以值传递的方式传入函数不同的是,&var传入的是函数的引用,也即是说,你在lambda函数里边对这个变量做出的操作是会改变原值的,而且传入的值是无需加mutable修饰也可以进行修改的。
以下是一段的示例的代码:
#include <bits/stdc++.h>
using namespace std;
int main(){
int a=100;
cout<<a<<endl;//此时打印结果为100
auto f=[&a](){
a*=20;
};
f();//此时a的值变为2000;
cout<<a;//此时打印结果为2000;
}
[=]表示以值传递捕获父作用域内所有变量(包括this指针):
也即是说,当捕获列表为‘=’时,可以捕获当前作用域内的所有变量,包括this指针,可以通过this指针访问父类中的成员函数,但由于是值传递的方式传入的,所以如果想要修改传入的值的话,需要添加mutable修饰。
以下是一段代码示例;
#include <bits/stdc++.h>
using namespace std;
int main(){
int a=0,b=100,c=300;
auto f1=[=](){return a+c+b;};//合法
auto f2=[=]()mutable{
c*=b;
a+=c;
return a;};//合法 mutable解除了值传递进函数的const属性。
/*
auto f3=[=]{c*=b;
a+=c;
return a;};*/ //违法的,值传递进去的变量是const属性的,不能改变。
cout<<f1()<<" "<<f2(); //将f3注释后可以运行 打印结果为400 30000
return 0;
}
[&]表示以引用的方式捕获父作用域内的所有变量(包括this指针)
与用引用方式捕获变量类似,[&]的意思是捕获父作用域内的所有内的所有变量,并以引用的方式传入lambda函数,也就是说修改lambda函数中的变量值可以对函数外的变量产生影响。
以下是一段示例代码:
#include <bits/stdc++.h>
using namespace std;
int main(){
int a=0,b=100,c=300;
auto f1=[&](){return a+c+b;};//合法
auto f2=[&]()mutable{
c*=b;
a+=c;
return a;};//合法
auto f3=[&]{c*=b;
a+=c;
return a;};//合法 以引用传入的变量不用mutable也能修改值
f1();
cout<<a<<" "<<b<<" "<<c<<endl; //打印结果为0 100 300
f2();
cout<<a<<" "<<b<<" "<<c<<endl; //打印结果为30000 100 30000
f3();
cout<<a<<" "<<b<<" "<<c<<endl; //打印结果为 3003000 100 3000000
return 0;
}
混合捕获列表:
//以下的捕获列表都是合法的
[&var,=]//先用引用传入var,再将其他变量以值传递的方式传递
[a,&]//先用值传递的方式传入,再将其他变量以引用的方式传递
[this]//把this指针传入lambda函数中
[a,b,&]//与上面相似,下同
[a,b,=]
[a,b,……,&]
[a,b,……,=]
mutable:
mutable意为可变的,意思是将允许在lambda函数中修改以值传递捕获的变量。
以下是一段示例代码
#include <bits/stdc++.h>
int main(){
int a=0,b=200,c=100;
auto f2=[=]()mutable{
c*=b;
a+=c;
return a;};//合法 mutable解除了值传递进函数的const属性。
/*
auto f3=[=]{c*=b;
a+=c;
return a;};*/ //违法的,值传递进去的变量是const属性的,不能改变。
return 0;}//值得一提的是,本段示例代码中并未调用f2
指定返回类型
在lambda函数中,返回类型是可以由编译器自动推演的,但是如果需要指定的返回类型,可以在mutable后面添加→数据类型来指定,以下是一段示例代码:
#include <bits/stdc++.h>
using namespace std;
int main(){
char c='a';
int a=1;
auto f1=[=](){
return c-a;
};
cout<<f1()<<" ";//打印96 因为默认的返回类型是int
auto f2=[=]()->char{
return c+a;
};
cout<<f2();//打印b 因为我们指明了返回类型是char
return 0;
}
语法总结:
综上,lambda的语法如下图所示
值得一提的是,可变规则与返回类型之前,可以进行异常的处理。
以下的部分是可以省略的:
-
可变规则(默认为可变的)
-
返回类型(默认编译器自动推演)
-
异常说明(若无声明,则无)
-
参数列表 注意 只有在将面世的c++23标准中,才可以省略不写参数列表
总结:
lambda 表达式是 C++11 最重要也最常用的一个特性之一,C# 3.5 和 Java 8 中就引入了 lambda 表达式。
lambda 来源于函数式编程的概念,也是现代编程语言的一个特点。C++11 这次终于把 lambda 加进来了。
lambda表达式有如下优点:
-
声明式编程风格:就地匿名定义目标函数或函数对象,不需要额外写一个命名函数或者函数对象。以更直接的方式去写程序,好的可读性和可维护性。
-
简洁:不需要额外再写一个函数或者函数对象,避免了代码膨胀和功能分散,让开发者更加集中精力在手边的问题,同时也获取了更高的生产率。
-
在需要的时间和地点实现功能闭包,使程序更灵活。
善用lambda表达式可以有效地提升你的代码结构。