新的C++ 11 标准已经向javascript等脚本语言学习了,支持匿名函数,好在笔者对javascript等脚本语言也比较熟悉,所以理解C++的匿名函数也比较容易。
匿名函数是指在一个函数体内的某个功能需要一个定义函数,但是又不想去命名这个函数的场合下现场即时定义的表达式,也就是指Lambda表达式,用于简化编程工作。
定义匿名函数的语法
[capture] (parameters) specifiers ->return_type { body }
现对该语法的各个部分解释如下:
1、 [capture] 捕获的外部变量列表
标示一个Lambda表达式的开始,必须存在,不能省略。通过逗号分隔各个捕获参数,可进行传值捕获或者引用捕获,lambda表达式与这些捕获的外部变量会构成一个闭包(Closure),外部变量为闭包的成员变量。capture参数对象只能使用那些定义Lambda
为止时Lambda所在作用范围内可见的局部变量(包含Lambda所在类的this指针)
# | 语法示例 | 说明 |
1 | [] | 没有任何capture参数对象,但不能省略 |
2 | [=] | 以值传递方式捕获Lambda所在范围内的所有局部变量。 |
3 | [&] | 以引用传递方法捕获Lambda所在范围内的所有局部变量。 |
4 | this | 函数体可以使用Lambda所在类的成员变量。在类的非静态成员函数中定义的lambda表达式可以通过捕捉this指针,来访问对象的成员变量和成员函数 |
5 | [a,&b] | a以值传递捕获,b以引用形式捕获。 |
6 | [=,&c] | c以引用形式捕获,其他变量以值传递捕获。 |
7 | [&,x] | x以值传递形式捕获,其他变量以引用形式捕获。 |
① 全局变量、静态变量不用显示写在外部变量列表中,可直接在lambda表达式中读写
② 传值捕获的外部变量是一个副本,默认情况下在lambda表达式中是只读的,若想修改该副本,需要在lambda表达式上添加mutable关键字 如:auto a2 = [Value](int x) mutable {Value++;};
2、(parameters):函数参数
参数列表,标识重载的()操作符,若lambda函数没有形参且没有被mutable等修饰,则参数的空圆括号可以省略,但笔者建议即使没有参数,括号不要省略,这样便于阅读代码。参数可以使用值传递和引用传递,与普通的函数参数没有区别。
与普通函数相比,lambda表达式的参数有如下限制
① 参数不能有缺省值 如:int Add1(int a, int b=10)
② 不能有可变长参数列表 如:int Add2(int count, ...)
③ 不能有无名参数 如:int Add3(int a, int b, int)
3、specifiers :mutable或者exception声明
可以省略。按函数参数按值传递时,加上mutable修饰后,可以修改传递进来的拷贝。
exception声明用于指定函数抛出的异常,可以使用throw(int)。
4、->return_type :返回值类型
标识函数返回值的类型,当返回值为void,或者函数体中只有一处return的地方时,这部分可以省略。
5、{ body }:匿名函数体
匿名函数的实现代码,以一对大括号包围,不能省略,函数体大括号内部可以为空。
6、示例
// 隐式返回类型
[](int x,int y) {return x+y;}
//没有return语句,Lambda的函数返回类型为 'void'
[](int& x) {++x;}
// 没有参数,访问全局变量num
[]() {++num;}
//省略参数
[] {++num;}
实例:
例如,在一个应用中,需要用库函数sort排序,sort行数需要有一个比较回调,当一个程序中,排序函数用的次数不多时,专门给排序回调函数起个名字污染命名空间似乎不合算,能不能不给它起cmp的名字,又能使用比较大小的功能呢?此时就可以使用匿名函数了。
#include <stdio.h>
#include <string>
#include <vector>
#include <algorithm> //for sort
int sort_test()
{
std::vector<int> data = { 6, 1, 12, 3, 14, 16, 8, 9 };
//from big to small
sort(data.begin(), data.end(), [](int& a, int& b)->bool {
return a > b;
});
cout << "Big->Small: ";
for (auto i : data)
cout << i << " ";
cout << endl;
//from small to big
sort(data.begin(), data.end(), [](int& a, int& b)->bool {
return a < b;
});
cout << "Small->Big: ";
for (auto i : data)
cout << i << " ";
cout << endl;
return 0;
}
int main(int argc,char * argv[])
{
sort_test();
return 0;
}