我想我的BLOG是不是要改名字了,还是C++相关。
想研究boost::lambda源码,找到这篇文章:http://www.cppblog.com/shifan3/archive/2006/06/09/8334.html 还算写得不错,通俗啊,我写不出这样的文章,本来是带着一个问题去的,就是关于函数对象的返回类型如何确定。嗯,先从我对lambda库实现的想法说起吧。
简单描述,lambda库是用函数对象重新对程序进行编码。什么是程序的编码,程序是什么,C++程序?包括三个方面:面向类类型的表达式(泛型的),函数(包括成员函数)和控制语句(if,then,while等)。lambda的结果是,让‘程序’重新以‘函数’的方式存在。靠,C++语言并不是函数式语言,整个库出来实现函数式,太牛了。
1种幻想,如果lambda获得了成功,成了新的C++标准,那可否从C++语言的层面直接支持函数,比如定义一个函数本身就在定义一个函数对象!。。。不太可能,这需要支持和改变的东西太多了,是彻底的改变,那还要那些函数式语言干嘛~~
转到lambda的实现,那三个方面又是以表达式为核心的,绑定后的函数还是可以构造表达式,控制语句完了还是可以构造表达式。它的最基础的技术就是表达式模板技术。
表达式模板技术又不好一言两语说清楚,我写过一个Vector的表达式模板,有一些感悟。首先,确定构造后的表达式用来干什么,比如数组的表达式,用来数值计算,所以要定义哪些接口,这个接口是自上而下调用的,不管表达式有多复杂,都会自上而下的调用。lambda的表达式目的很明确,用来函数调用,所以要有operator()接口,那么它又多了一条本质,lambda表达式是一个函数对象。那你要问它有多少个参数?返回参数如何确定?多参数应该是不确定的,没有很好的方法,只能是定义足够用那么多,用宏控制,比如我定义最多十个参数的operator(),那就要从operator()(A1)一直定义到operator()(A1,A2,...A10)这么多。返回类型迟一点再说。
然后是设计托管的泛型的运算符类,比如:
template<typename Arg1,typename Arg2>
class Mult
{
Arg1 arg1;
Arg2 arg2;
};
表示运算符*的托管类,它有两个泛型的参数成员,这里有一个问题,构造问题,我们的目标是要让它‘轻量级’构造,用引用,引用构造,引用的本质是取地址,那对于一些常量,比如程序中的123,就不能取地址。我们用一个trait,它到底是一个引用还是原来类型,从trait里获得。此外,常量值我们最好还用一个类来专门托管。
所有的运算符托管类和常量托管类,都要是一个接口一致的函数对象。然后再构造表达式编码。
如果只有一种运算符托管类,我们要重载多少个它的运算符操作?如果是二元运算符,以Mult为例,包括Mult*Mult,Mult*常数,常数*Mult,三种。如果增加一种,比如Add,那要重载多少?增加的有:Add*Add,Add*常数,常数*Add,Add*Mult,Mult*Add,还有Add+Add,Add+常数,等,非常之多。如果再多重载一些,恐怕就算用宏生成代码也要一大堆代码,所以再在这之上包装一下,我的Vector里用的是Exp:
template<typename Rep>
class Exp : public Rep
{
public:
Exp(Rep const& r): Rep(r) {}
Rep& rep() { return *(Rep*)this; }
};
让它继承至Rep是很好的选择,即继承了Rep的所有接口,如果做成员,那还要在这里添加接口的实现了。Exp<T>本质即是T,有T的接口,也只有T在其内部,于表于里都是T。有了它就可以简化运算符的重载了,让所有的运算符都针对它重载即可。成员rep()是还原函数,解除封装,将*this指针简单转型即可,也没有其它浪费的内存。
关于占位符_1,_2等,它们是特殊的函数对象,对它们调用operator()的时候返回第X个参数的引用即可。
好了,到了关键的返回值类型了,这很头疼,比如Mult的operator()如下:
template<typename A1,typename A2>
XXX operator()(A1 const& a1, A2 const& a2);
两个什么玩艺相乘,结果是什么类型和这是两个什么玩艺有关,比如:1<<10和cout<<10,同样是operator<<,前者的返回类型是整型,而后者是ostream&。很无奈只能用trait,所以我才想能不能用typeof这样的好东西:
幻想中的typeof:
typeof(表达式)
在编译期计算表达式的返回类型,当然也可以模板化,最终具现的时候一一计算。这很容易实现,比如A1=Vector<int>,A2=int,然后又查找到Vector<int> operator*(Vector<int> const&,int);的申明,所以typeof(A1*A2)就是Vector<int>,所以就可以这样简单的写:
template<typename A1,typename A2>
typeof(A1*A2) operator()(A1 const& a1, A2 const& a2);
返回类型应该和运算符类型相关,如果用trait来实现的话,但是为了设置自己的类型的返回类型又要专门放在外面好一些。
最后一个问题是const和非const的问题,也很苦恼,简单的解决方法是统一一下。
评论