C++ 11中的Lambda表达式
C++ 11中的Lambda表达式用于定义并创建匿名的函数对象, 函数式变成风格也融入到C++中。 Lambda 的函数显得与以前的C++规范下的代码风格有些差别,先看一个简单的例子;
int main() { int girls = 3; int boys = 4; auto totalChild = [](int x, int y)->int { return x + y; }; return totalChild(girls + boys); }
如上代码,我们定义了一个lambda 函数, 该函数接受两个参数(int x,int y),并返回其值。 直观的说, lambda 函数跟普通函数相比不需要函数名(匿名函数), 取而代之的是一对[]。此外,lambda函数还采用了追踪返回类型的方式生命返回值。
lambda 函数的定义如下
[capture] (parameters) mutable ->return_type {statement }
其中:
Capture: 捕捉列表,它总是出现在lambda函数的开始,事实上,[] 是lambda函数引出符。捕捉列表由多个捕捉项组成,并以逗号分开,捕捉列表有如下几种形式:
[var] 表示以值传递方式捕捉变量var
[=] 表示以值传递方式捕捉所有父作用域的便利(包括this)
[&var] 表示以引用的方式捕捉变量var
[&] 表示以引用传递捕捉所有父作用域的变量(包括this)
[this] 表示以值传递方式捕捉当前的this 指针
一些常见组合:
[=, &a, &b] 表示以引用传递a 和b,值传递其他的所有变量。
[&, a, this] 表示也值传递a 和this, 引用传递其他所有变量。
需要注意的是捕捉列表不允许变量重复传递, 下面就是一些典型的重复, 会导致编译时错误
[=,a] 这里= 已经以值传递所有变量,a 是一种重复
[&, &this] 这里& 已经以引用传递所有的变量, 捕捉&this 是一种重复。
Parameters:
参数列表。 与普通函数的参数列表一致。如果不需要参数传递,则可以省略()。
mutable:
mutable 修饰符。 默认情况下, lambda 函数总是一个const 函数, mutable 可以取消其常量性。 在使用该修饰符的时候, 参数列表不可为空(即使参数为空)
->return_type
返回类型。用于追踪返回类型形式声明函数的返回类型。处于方便,不需要返回值的时候也可以连同->一起省略, 此外,在返回类型明确的情况下, 也可以省略该部分, 让编译器对返回类型进行推断。
{statement}
函数体。 与普通函数一致,不过除了使用参数之外,还可以使用所有捕获的变量
关于lambda 的一些注意事项
使用lambda函数的时候,捕捉列表不同会导致不同的结果。 具体来讲,就是按值传递和按引用传递的方式捕捉列表的效果是不一样的。 对于值传递的捕捉列表, 其传递的值在lambda 函数定义的时候已经决定好了。 而按引用传递,其传递的值则等于lambda函数被调用的值。以下的例子演示这个不同:
#include <iostream> using namespace std; int main(int argc, char* argv[]) { auto by_val_lambda = [=] { return j + 1; }; auto by_ref_lambda = [&] { return j + 1; }; cout << "by_val_lambda: " << by_val_lambda() << endl; cout << "by_ref_lambda: " << by_ref_lambda() << endl; j++; cout << "by_val_lambda: " << by_val_lambda() << endl; cout << "by_ref_lambda: " << by_ref_lambda() << endl; }
程序输出:
by_val_lambda: 13
by_ref_lambda: 13
by_val_lambda: 13
by_ref_lambda: 14
第一次调用by_val_lambda 和by_ref_lambda 时,其运算结果并没有不同,两者均为 12 + 1 = 13. 但第二次调用by_val_lambda的时候, 其计算的是12 + 1 = 13, 相反地,第二次调用by_ref_lambda时计算的是 13 + 1 = 14. 原因在于by_val_lambda中,j被视为常量,一旦初始化以后就不会改变(可以认为之后的只是一个跟父作用域中j同名的常量)而在by_ref_lambda中, j 仍作为父作用域中的值。
在使用lambda函数的时候,如果需要捕捉到值成为lambda 函数的常量,应该按值传递的方式捕捉,反之, 需要捕捉的值称为lambda 函数运行时的常量,则应该使用引用捕捉。