Introduction
C ++ 11引入了lambda,该lambda提供了一种语法上轻量级的方式来动态定义函数。它们还可以通过值或引用来捕获(或封闭)周围范围的变量。在本文中,我们将研究lambda与纯函数和函子类(实现的类)在实现方面的区别operator()。
Capture By Value
1 | class Functor { |
按值捕获lambda的工作方式与标准functor几乎相同:它们都分配存储捕获值的对象,并获取指向该对象的隐藏函数参数。lambda和functor的函数调用执行的代码是相同的。唯一的区别是lambda的构造函数被内联到创建lambda的函数中,而不是像函子的构造函数那样是一个单独的函数。
Capture By Reference
1 | class Functor { |
当通过引用捕获时,functor和lambda对象包含一个指针而不是一个值,这表明引用的行为是在内部是使用指针实现的。与按值捕获一样,functor和lambda调用代码是等价的,但是lambda的构造函数是内联的,而functor的则不是。
结论
C ++ lambda和函子比相似之处更多。这是预料之中的;lambda的主要目标是成为创建函数和闭包的语法上简单的方法。即使没有捕获任何变量,它们也与普通函数略有不同。总结主要区别:
- 1、函子和lambda总是传递一个this指针,而普通函数自然不是。这会消耗一个额外的寄存器和8个字节的堆栈空间。
- Lambda“构造函数”被内联到创建Lambda的函数中。这显着减少了执行的复制量(lambda的2条指令,函子的5条指令),以及避免了函数调用的建立和拆卸。
闭包 利用函数对象记住状态数据
虽然函数对象也可以像函数一样被用来表达一个数据处理过程,但它更大的意义在于,函数对象具有“记忆力”,它可以记住函数执行过程中的状态数据,从而使它可以应用在那些需要记住函数上次执行的状态数据的场景下。对于普通函数而言,函数只是用来表达一个运算的过程,它无法记住运算过程中的一些状态数据。函数就像一个漏斗,数据可以从这个漏洞中流过,发生某些变化,但是这个漏斗什么都不会留下。在大多数情况下,“漏斗式”的普通函数已经完全可以满足需要了,但在某些特殊情况下,下一次的函数执行是在上一次函数执行的结果基础上进行的。这时,函数就需要记住上一次的执行状态数据以备下一次函数执行使用。
函数对象的出现就是用来弥补函数的这个缺陷的。利用函数对象自身的成员变量,函数可以记住在每次执行过程中的状态数据,找回失去的记忆。
std::bind和闭包
在函数式编程中,通过组合现有的函数,我们可以创造出新的函数。标准库中的std::bind就是可以创造闭包(closure)的工具。
1 | class Foo |
通过std::bind,我们可以为同一个类的不同对象可以分派不同的实现,从而实现不同的行为。这种方式使得我们不在需要设计通过继承与虚函数来实现多态,无疑为程序库设计提供的新的方式。