晚上突发奇想,想用C++实现一个无比简陋的lambda:只能作为匿名函数,不带闭包,无捕获,所有中间值和结果都是int,利用C++98实现。
这类lambda实际上就是Eigen这类库中延迟计算的一个原型,写起来并不复杂,大约100行左右代码就能完成。
测试例子:
分析
我们最终要实现的一个东西,我们希望它具有的特性是:
- 具有
int operator() (const int, const int)
,能当作函数使用 - 能在
+
、<
运算符下和整数和自身的类运算,产生一个新的这类对象
之前我们分析过:对于这类抽象,如果需要运行时多态,就应该使用继承与虚函数;如果不需要,就应该使用静态多态。
动态多态
先尝试一下动态多态的思想:
定义接口:
对于+
,返回一个继承自lambda_interface
的子类:
这里需要注意的是clone,众所周知构造函数不能是虚函数,但是复制构造可以利用clone()
来变相实现虚函数的效果。
定义两个placeholder
,它们始终返回第1/2个参数:
对于常数,我们构造一个constant
类把它包裹起来:
测试:
迄今为止,我们已经实现了动态多态的+
,我们发现,这一实现存在着诸多不足:
+
中的常数暂时还不能自动转换成constant类型,需要手动包裹。之后的改进手段就是重载+(const lambda_interface&, const int)
等- 较为丑陋,这其中所有信息都可以编译时获得,引入了虚函数增加了大量不必要的负担,并且中间堆上分配了不少内存,容易造成性能问题。
静态多态
静态多态不利用语法上的继承来实现is_a
的特性描述,而是利用模板+各个类相同的接口来实现一种语法在不同类上不同的表现。
例如:
这样就实现了语义上的多态性。
实现:
这个不是接口,而是为了减少代码量而把共同部分抽出来了而已:
因为一个运算符其两个操作数的类型没有统一的接口,所以利用模板来生成基类。
+
类:
+
函数也要利用模板来生成,注意:这里把int
替换成了constant
类:
replace<T>::type
如果T
不是int
,返回自身,否则返回constant
constant
和placeholder
:
静态多态版本完成(上面省略了<
和>
,和+
一样处理就行了
总结
以上全部代码的完整版都能在:https://github.com/htfy96/CppToys/tree/master/lambdatest 看到
静态多态在不需要运行时信息时,不仅降低了overhead,而且给程序编写带来了很大的简洁。在这种模式下,C++中的类型可以当作duck typing
的方式来使用,从而给了程序员很大的自由。 不过,缺点也在于弱类型约束一旦出现bug很有可能会产生大量无用报错信息,缓解这一缺点可以使用SFINAE
,或是进入C++11年代的static_assert
(你都有了C++11了手写什么lambda),或是未来C++17的concept
。
总之,C++不强迫程序员选择什么,这就是为什么我非常喜欢这门语言。
Self-promotion
SJTU大二本科生,寻求暑假实习中。偏向于后端,有利用Golang+redis开发的经历。
Linkedin:http://cn.linkedin.com/in/vicluo