[函数对象参数],例如[&,a,b]标识一个Lambda的开始,这部分必须存在,不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。
函数对象参数有以下形式:
[ ] 空没有使用任何函数对象参数。
[=] 函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。
[&] 函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)。
[this] 函数体内可以使用Lambda所在类中的成员变量。
[a] 将a按值进行传递。按值进行传递时,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加mutable修饰符。
[&a] 将a按引用进行传递。
[a, &b] 将a按值进行传递,b按引用进行传递。
[=,&a, &b] 除a和b按引用进行传递外,其他参数都按值进行传递。
[&, a, b] 除a和b按值进行传递外,其他参数都按引用进行传递。
(操作符重载函数参数),例如(int a,int &b)标识重载的()操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如:(a,b))和按引用(如:(&a,&b))两种方式进行传递
mutable与exception声明,例如 mutable throw(),可省略
按值传递函数对象参数时,加上mutable修饰符后,可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身,如果没有添加mutable,相当于对函数参数的增加了const修饰,无法修改参数)。exception声明用于指定函数抛出的异常,如抛出整数类型的异常,可以使用throw(int)。
示例:
->返回值类型,例如 ->int 表示返回 int类型
标识函数返回值的类型,当返回值为void,或者函数体中只有一处return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。
{函数体},例如{cout<<“abc”;},不可省略,可以为空
// 无函数对象参数,输出:1 2 { for_each(vctTemp.begin(), vctTemp.end(), [](int v){ cout << v << endl; }); }
// 以值方式传递作用域内所有可见的局部变量(包括this),输出:11 12 { int a = 10; for_each(vctTemp.begin(), vctTemp.end(), [=](int v){ cout << v+a << endl; }); }
// 以引用方式传递作用域内所有可见的局部变量(包括this),输出:11 13 12 { int a = 10; for_each(vctTemp.begin(), vctTemp.end(), [&](int v)mutable{ cout << v+a << endl; a++; }); cout << a << endl; }
// 以值方式传递局部变量a,输出:11 13 10 { int a = 10; for_each(vctTemp.begin(), vctTemp.end(), [a](int v)mutable{ cout << v+a << endl; a++; }); cout << a << endl; }
// 以引用方式传递局部变量a,输出:11 13 12 { int a = 10; for_each(vctTemp.begin(), vctTemp.end(), [&a](int v){ cout << v+a << endl; a++; }); cout << a << endl; }
// 传递this,输出:21 22 { for_each(vctTemp.begin(), vctTemp.end(), [this](int v){ cout << v+m_nData << endl; }); }
// 除b按引用传递外,其他均按值传递,输出:11 12 17 { int a = 10; int b = 15; for_each(vctTemp.begin(), vctTemp.end(), [=, &b](int v){ cout << v+a << endl; b++; }); cout << b << endl; }
int temp = 10;
vector<int> ivec = {30, -10, -20, 50, 40 ,100, -50};
std::sort(ivec.begin(), ivec.end(), [](const int &x, const int &y) {return abs(x) < abs(y);});
std::for_each(ivec.begin(), ivec.end(), [&](int &x) { x += temp; cout << x << endl;});
三、注意事项:
比较上面三种方式,有一些细节需要注意:
1. closure的状态特指其运行的上下文。 closure将存贮它运行时需要的上下文,从而保证在closure创建时的上下文可以在closure运行时依然有效。
比如round就是closure的上下文。保存上下文的这一特点通常被称作“capture”或者是"bind"。 capture可以自己写,比如MyFuctor f(round); 也可以用boost::bind。
当然最方便的还是让编译器帮你自动完成。编译器将自动识别closure用到的变量,然后创建一个匿名的类,将这个变量保存到匿名类的成员变量中。
C++中有两种capture方式,by value和by reference。写法是[=]和[&]。
需要注意的是,capture by reference是不会修改被capture变量的生命周期的,你要保证被capture的变量在closure运行时是有效的。
这一点不像Java,Java中变量被capture的话,就变成被引用了,从而GC不会回收它。
2. closure的类型是隐藏的,每次创建一个closure,编译器都会创建一个新的类型。
如果你想保存一个clousre时就不是那么直接,因为你不知道它的类型。这时那需要一些模板技巧,可参考boost::function的实现。
简单的方式是直接用std::function来保存。
std::function<int(float)> closure;
closure = [](float f) { return 0.0f };
closure = [](float f) { return 1.0f };
四、闭包(匿名函数)用处,可以是流程更清晰,易于理解,一般不能单独使用,必须有上下文,闭包里处理的是上下文中的一些变量。一般情况下不能单独使用
auto Do=[&]()
{
}
auto nextDo=[=](){
}