虽然说不会lambda表达式并不影响工作赚钱,但是学会了它,在编程中确实可以增色不少。那么在C++中lambda表达式该怎么使用呢?
C++ 11 中的 Lambda 表达式用于定义并创建匿名的函数对象,以简化编程工作。
[ capture list ] (parameters) -> return-type { method definition}
上述的这行加粗斜体,就是lambda表达式的语法形式,当然这玩意看起来十分抽象,我们不妨搞一个简单地例子来看一下。
先来看这么一个简单的C++程序,无非是主程序中执行print函数,print函数中输出hello world!,那么现在就将这个程序改写为lambda表达式;
#include
using namespace std;
void print(){
cout << "hello world!" << endl;
}
int main(){
print();
cin.ignore();
return 0;
}
如下图所示,这就是一个我们改写后的简单的lambda表达式
#include
using namespace std;
int main(){
auto print = []{cout << "hello world!" << endl; };
print();
cin.ignore();
return 0;
}
我们专门截了个图,来看一下普通函数写法与lambda表达式的异同。
1.我们可以看到两个大括号内部,两者都是相同的,没有任何差异
2.虽然lambda表达式号称匿名函数,但是同样我们也可以利用auto推导一个函数名
3.在函数执行的写法,两者也可以做到保持一致。
当然lambda表达式也可以直接写为:
[]{cout << “hello world!” << endl; }();
一行搞定,这就是lambda表达式的便利之处。
#include
using namespace std;
int main(){
[]{cout << "hello world!" << endl; }();
cin.ignore();
return 0;
}
了解了,比较简单的lambda表达式,那么问题来了,如何将一个带参的函数改写为lambda表达式。
#include
#include
using namespace std;
void print(string str){
cout << str << endl;
}
int main(){
print("hello world!");
cin.ignore();
return 0;
}
一个简单的改变
#include
#include
using namespace std;
int main(){
auto print = [](string str){cout << str << endl; };
print("hello world!");
cin.ignore();
return 0;
}
如图所示,我们可以看到带参,其实也很好办,只要在[]{}中间加上(),()里面写上形参即可。
其实观察我们再最开始提到的语法形式,()这个小括号本来就是在这个位置,只是在无参的情况下,我们可以将其省略。
除了参数以外,一个函数还要返回值,那么返回值该怎么办呢?例如下面的代码:
#include
#include
using namespace std;
string print(string str){
return str;
}
int main(){
cout << print("hello world!") << endl;
cin.ignore();
return 0;
}
只需这样子写:
#include
#include
using namespace std;
int main(){
auto print = [](string str)->string{return str; };
cout << print("hello world!") << endl;
cin.ignore();
return 0;
}
我们来看一下区别:
无非是在{}中增加了->string罢了。
没错,->返回值类型{};这个就是带返回值的写法了。
lambda表达式,还有一大便利,就是它可以捕获函数体中的变量
一个简单示例:输出结果为3;
#include
using namespace std;
int main(){
int a = 0;
auto print = [&a](){ int b = 3; a = b; };
print();
cout << a << endl;
cin.ignore();
return 0;
}
注:[]中相关说明:
[] 不捕获任何变量
[&] 以引用方式捕获所有变量
[=] 用值的方式捕获所有变量(可能被编译器优化为const &)
[=, &foo] 以引用捕获foo, 但其余变量都靠值捕获
[&, foo] 以值捕获foo, 但其余变量都靠引用捕获
[bar] 以值方式捕获bar; 不捕获其它变量
[this] 捕获所在类的this指针
还是上一个程序,做一个简单的修改,我们可以看到,在这个程序中对a的只进行了操作或者说修改,这个程序运行无误,结果输出为1;
#include
using namespace std;
int main(){
int a = 0;
auto print = [&a](){ int b = 3; a++; };
print();
cout << a << endl;
cin.ignore();
return 0;
}
那么这样子写呢?不难看出,代码从&a变为了a;
#include
using namespace std;
int main(){
int a = 0;
auto print = [a](){ int b = 3; a++; };
print();
cout << a << endl;
cin.ignore();
return 0;
}
大家运行一下?没错,这个代码是错误的。
因为,正常情况下,lambda表达式是不允许对这个a进行修改的,想要修改就需要加入mutable关键字。
这个时候我们可以发现输出结果为0,这是因为,我们是值捕获,而不是引用捕获,输出的还是定义a的值。
无可争议,最令人感动的是,在STL这类代码的编程时,lambda表达式简直是绝配:
举例:
在没有lambda表达式的时候,有一个vector,如何遍历操作这个vector?
vector name = { “小明”, “小李”, “小王”, “小高” };
我在这里列举三种方式,当然方法肯定是不止三种的:
方法一:
for (int i = 0; i < name.size(); i++){
cout << name[i] << " ";
}
方法二:
for each (string s in name){
cout << s << " ";
}
方法三:
for (string s : name){
cout << s << " ";
}
那么有了lambda表达式这个遍历该怎么写呢?
一行即可解决问题:
for_each (name.begin(), name.end(), [](string str){cout << str << " "; });
测试代码如下:
#include
#include
#include
#include
using namespace std;
int main(){
vector<string> name = { "小明", "小李", "小王", "小高" };
for_each (name.begin(), name.end(), [](string str){cout << str << " "; });
cin.ignore();
return 0;
}
当然这个简单的示例,还不能将lambda表达式的魅力完整展现,有兴趣的朋友看完可以自己尝试一下。
最后做一个总结吧,
看到最后,大家应该也发现了吧,其实刚开始给的形式是有那么一丢丢问题的,应该是这样的。
[capture list] (params list) 关键字-> return type {function body}
captrue list:捕获外部变量列表(选填)
params list:形参列表(可省略)
关键字:mutable,exception,attribute(可省略)
return type:返回类型(可省略)
function body:函数体
其中:
mutable说明lambda表达式体内的代码可以修改被捕获的变量,并且可以访问被捕获的对象的non-const方法。
exception说明lambda表达式是否抛出异常以及何种异常。
attribute用来声明属性。