前些天买了本《程序设计语言理论》,看了简介,Lambda 演算贯穿整个理论,尤其在函数式语言中具有重要作用。C++11 中也加入了 Lambda 表达式,下面做个总结。
1. 一个简单的例子
一个简单的 Lambda 表达式如下:
这就定义了一个对象,这个对象匿名,再强调一下,Lambda 表达式是对象,不是类型。本例中,该对象的类型是 ‘anonymous-namespace’::<lambda0>,这是编译器给它设的一个类型名。
2. 实现
一般地,编译器实现 Lambda 表达式时,将其转化为函数对象(仿函数 functor)。比如上面的例子将转化为
其中 A() 是一个函数对象,而 A 是类型,这些要明确区分。
3. 状态
仿函数和 Lambda 表达式是可以有状态的,也就是说它们可以含数据成员,而函数指针就无状态可言,比如下面这种情况就无法用函数指针,只能用仿函数或 Lambda 表达式。
程序中调用 for_each 时,只输出大于某个数的元素,而这个数事先不知道,这个数代表了仿函数的状态,而若用函数指针则无法获得这个数(状态),注意 for_each 的第三个参数是一元的。
4. 捕捉列表中的值传递与引用传递
按值方式传递捕捉列表与按引用方式传递列表不同,以下面的程序为例:
要解释这个问题,把它们转化为仿函数(编译器的实现)就一目了然了:
值传递时 Lambda 保存了一个副本,与形参就无关了。引用传递时,Lambda 保存的是形参的引用,所以后面改变形参 ++i 时,Lambda 内保存的引用指向的 i 变了(同一个 i),注意指针与引用类似,用指针来解释引用再好不过了。
5. 捕捉列表的限制
Lambda 捕捉列表仅能捕捉父作用域的自动变量。
标准是这么定义的,但有的编译器不遵守,笔者对具体的规则也存有疑惑。但总的来说,Lambda 的思想是局限于一个局部作用域中使用,若需全局共享,则用函数指针(无状态)或仿函数(有状态)。
参考资料:《深入理解 C++11》
(End)