语法定义
[capture] (parameters) mutable -> return-type {statement}
- [capture]:捕捉列表。[]是lambda表达式的引出符。lambda表达式可以自由访问全局变量和static变量,但对于局部变量和成员变量需要捕捉变量支持。capture描述了上下文可以供函数体使用的变量及其使用方式(值传递或引用传递)。[capture]有以下几种基本形式:
表达式 | 意义 |
---|---|
[var] | 值传递方式捕捉var变量 |
[=] | 值传递方式捕捉lambda表达式所在函数位置可以访问到的所有局部变量,和lambda表达式所在类的所有成员变量 |
[&var] | 引用传递方式捕捉var变量 |
[&] | 引用传递方式捕捉lambda表达式所在函数位置可以访问到的所有局部变量,和lambda表达式所在类的所有成员变量 |
[this] | 值传递方式捕捉lambda表达式所在类的所有成员变量 |
此外,还可以组合上面的基本形式,来按更复杂的形式捕捉变量,例如:
表达式 | 意义 |
---|---|
[=, &a, &b] | 按引用传递方式捕捉变量a、b,其他变量按值传递方式捕捉 |
[&, a, this] | 按值传递方式捕捉变量a和类成员变量,按引用传递方式捕捉其他变量 |
但是,组合方式中,同种传递方式中不能存在包含关系:
表达式 | 意义 |
---|---|
[=, a] | =和a都是按值传递,且=包含了a |
[&, &this] | &和this都是按引用传递,且&包含了&this |
- (parameters):参数列表,如果不需要传递参数可省略。
- mutable:修饰符。默认情况下,按值传递方式捕捉的变量(注意不是参数列表中的传参)是const,不可对其进行修改,mutable修饰符可以取消其常量性。当使用了mutable修饰符时,参数列表不可省略。
- ->return-type:返回类型,不需要返回值时可省略。
- {statement}:函数体。
lambda表达式是函数对象的语法糖
lambda表达式的本质是函数对象。现阶段,编译器通常把lambda表达式转换为一个函数对象:
值传递和引用传递
捕捉列表中值传递的变量,其传递的值在lambda表达式定义时已经确定;而引用传递的变量,其传递的值等于lambda函数调用时的值。这可以用lambda表达式到函数对象的转换来解析。
int i = 10;
auto by_val_lambda = [=] { return i + 1; };
auto by_ref_lambda = [&] { return i + 1; };
cout << "by_val_lambda: " << by_val_lambda() << endl;
cout << "by_ref_lambda: " << by_ref_lambda() << endl;
i++;
cout << "by_val_lambda: " << by_val_lambda() << endl;
cout << "by_ref_lambda: " << by_ref_lambda() << endl;
运行结果
by_val_lambda: 11
by_ref_lambda: 11
by_val_lambda: 11
by_ref_lambda: 12
lambda表达式的常量性
前面说到,lambda表达式的本质是函数对象,更准确地说应该是operator()为常成员函数的函数对象(默认情况),捕捉列表的参数对应函数对象的成员变量。而常成员函数不能修改任何非静态成员变量,所以默认情况下lambda不能修改捕捉列表中按值传递的变量。而按引用传递的变量可以在lambda中修改,因为并没有改变引用本身,而是改变了引用指向的对象。
mutable修饰符可以移除lambda的常量性。
lambda表达式和普通函数在STL中的应用
STL中部分算法可以通过回调函数指定内部实现,但是函数签名是固定的。普通函数和STL算法搭配使用时,普通函数无法获取到额外的信息,而lambda可以通过捕捉列表来实现这一需求:
// 利用for_each算法输出vector中大于ubound的值
void above_fun(int i, int ubound)
{
if (i > ubound)
cout << i << endl;
}
int main()
{
vector<int> vec = { 1, 2, 3, 4, 5, 6 };
int ubound = 3;
auto above_lambda = [ubound](int i) {
if (i > ubound)
cout << i << endl;
};
for_each(vec.begin(), vec.end(), above_lambda);
// 函数签名不匹配for_each第三参数,不能使用for_each算法
for (auto iter = vec.begin(); iter != vec.end(); iter++)
above_fun(*iter, ubound);
}
lambda表达式相对于普通函数,在STL算法中的使用更加灵活,适用性更广。而且lambda表达式默认内联,相比普通函数更高效。
实际上,可以利用STL提供的函数适配器来改变函数可调用对象的参数个数。也就是,above_fun函数通过函数适配器,可以搭配for_each算法使用,但是代码通常比较晦涩:
for_each(vec.begin(), vec.end(), bind(above_fun, _1, ubound));