C++11新特性——你所需要知道的关于匿名函数的一切
概述
- 在很多编程语言中,都存在着匿名函数,而C++也在11版本以后加入了这个新特性
- 顾名思义,匿名函数就是不用写函数名的函数,这要就解决了起函数名这个烦人的问题啦(当你只使用一次时)
- 接下来看看格式是怎样的
[捕获列表](参数列表)->返回类型{函数体}
[](int a,int b)->int{return a+b;}
auto f=[](int a,int b){return a+b;};
- 我一开始并不理解,如果不给这个函数起名字的话,我该如何去调用它呢?
- 这里给出两种办法,一种是立刻调用,但以后再也不能调用了;另一种就是把这个函数当成一个变量,给它起一个名字。
int ans=[](int a,int b)->int{return a+b;}(10,54);
auto f=[](int a,int b){return a+b;};
f(10,90);
- 细心的朋友会发现,我在上面那个例子中有一个没有指定函数返回值,这是可以的,可以由编译器自动推断出类型
- 接下来,我们来说说mutable关键字和捕获列表
细说捕获列表
- 一开始我并不理解,为啥要有一个这么奇怪的东西存在
- 现在我大概了解了,是因为匿名函数往往都声明在一个函数内部,比如经常出现在main函数内部
- 此时,这个函数在main函数的作用域内,但如果不传入参数,则main函数中的其余变量该匿名函数都是访问不到的
- 其实我认为捕获列表就是一个参数列表,只不过在使用上可以更加方便,例如你要使用很多本地变量,你不需要一个个去传参
- 下面举一个例子
int main()
{
int t=10;
auto f=[](){cout<<"t="<<t<<endl;};
f();
return 0;
}
- 而假如你在参数列表中加入了这个变量,函数内部就可以访问到了,就像下面这样
int main()
{
int t=10;
auto f=[t](){cout<<"t="<<t<<endl;};
f();
return 0;
}
- 接下来,我们来思考这个t的值在函数内部会不会改变呢?
- 我们来做个实验
int main()
{
int t=10;
auto f=[t](){cout<<"t="<<t++<<endl;};
f();
return 0;
}
- 这样是会报错的,编译器会说表达式必须是一个可以修改的左值
- 那你肯定就会想,如果想修改,可不可以传指针或者引用呢?
- 答案是:可以滴!!
int main()
{
int t=10;
auto f=[&t](){cout<<"t+1="<<++t<<endl;};
f();
cout<<"t="<<t<<endl;
return 0;
}
- 但是,目前不支持指针传递
- 你现在也许会说,这和参数列表有啥区别,接下来,我们来看看这个捕获列表到底可以写点啥
捕获形式 | 说明 |
---|
[] | 不捕获任何外部变量 |
[变量名,…] | 默认以值的形式捕获制定的外部变量(逗号分割) |
[this] | 以值的形式捕获this指针 |
[=] | 以值的形式捕获所有外部变量 |
[&] | 以引用的形式捕获所有外部变量 |
[&, a] | 以值的形式捕获外部变量a,以引用的形式捕获其他外部变量 |
[=, &a] | 以引用的形式捕获外部变量a,以值的形式捕获其他外部变量 |
细说mutable关键字
- 你肯定会问,哪来的mutable关键字?那是因为我还没有给出匿名函数的完整表达形式
[capture_list](params_list) mutable exception->return type {function_body}
- 学过c++面向对象的同学们应该知道,在一个类中,可以声明一个常函数,常函数不可以修改成员变量,除非这个成员变量使用了mutable关键字来修饰(我都快忘光了)
- 同样的,匿名函数也是一个常函数,他默认是不可以修改捕获列表中变量的值的,除非在这个匿名函数参数列表后面加上mutable关键字
- 这里,我们仅针对捕获列表为值传递的情况
- 注意,mutable关键字可以省略,如果要写,则必须写()来代表参数列表,哪怕是无参函数
int main()
{
int t=10;
auto f=[t]() mutable {
t+=10;
cout<<"t="<<t<<endl;};
f();
f();
cout<<"t="<<t<<endl;
return 0;
}
- 我们可以看到,mutable生效了,我们可以修改t的值了
- 且细心的人会发现,这并不影响匿名函数外面t的值
- 且在函数内部,这个t的值就像被static关键字修饰了一样,这个变量的生命周期变得像main函数一样了