boost::function和boost:bind是一对强大的利器。相信用过的童鞋多少有些体会。html
虽然平时在用boost::function,可是用的时候心中总会一些不安,由于不知道它是怎么实现的。因而,就本身琢磨着简单的实现一下,搞明白基本的原理。函数
对于这个简单实现,有如下几个目标:测试
首先,定义一个基类:spa
template<typename R, typename T1, typename T2> class base { public: virtual ~base() { } virtual R operator()(T1, T2) = 0; };
而后再实现一个普通函数/函数指针的版本:指针
template<typename R, typename T1, typename T2> class func : public base<R, T1, T2> { public: func(R (*ptr)(T1, T2)) : ptr_(ptr) { } virtual R operator()(T1 a, T2 b) { return ptr_(a, b); } private: R (*ptr_)(T1, T2); };
接着,实现支持成员函数指针的版本:htm
template<typename R, typename Class, typename T> class member : public base<R, Class, T> { }; template<typename R, typename Class, typename T> class member<R, Class*, T> : public base<R, Class*, T> { public: member(R (Class::*ptr)(T)) : ptr_(ptr) { } virtual R operator()(Class* obj, T a) { return (obj->*ptr_)(a); } private: R (Class::*ptr_)(T); };
可能有的童鞋要问,为何这里要有一个空的member类呢?这个问题放到下面解释。对象
天然的轮到最后一个种状况,函数对象/boost::bind类型的了:blog
template<typename T, typename R, typename T1, typename T2> class functor : public base<R, T1, T2> { public: functor(const T& obj) : obj_(obj) { } virtual R operator()(T1 a, T2 b) { return obj_(a, b); } private: T obj_; };
最后,就是可用的function类了,实现以下:get
template<typename T> class function { }; template<typename R, typename T1, typename T2> class function<R (T1, T2)> { public: template<typename Class, typename _R, typename _T2> function(_R (Class::*ptr)(_T2)) : ptr_(new member<R, T1, T2>(ptr)) { } template<typename _R, typename _T1, typename _T2> function(_R (*ptr)(_T1, _T2)) : ptr_(new func<R, T1, T2>(ptr)) { } template<typename T> function(const T& obj) : ptr_(new functor<T, R, T1, T2>(obj)) { } ~function() { delete ptr_; } virtual R operator()(T1 a, T2 b) { return ptr_->operator()(a, b); } private: base<R, T1, T2>* ptr_; };
你们可能注意到了,和前面的member类同样,function也有一个空的类,那么这些有什么用呢?编译器
这么作的缘由,主要是利用模板偏特化来进行类型萃取,正常的function声明的时候,好比function<int (int, int)>而不是func<int, int, int>。因此用模板的偏特化的版本
template<typename R, typename T1, typename T2> class function<R (T1, T2)>
就能够把int (int, int)萃取为R = int,T1 = int,T2 = int了。
同理,对于member类,因为通常咱们将成员函数指针绑定到function的时候,好比int function(Type*, int),其中Type是成员函数所属类。也就是说在function中的成员ptr_的类型是base<int, Type*, int>,那么在function的构造函数中构造的member类的类型就是member<int, Type*, int>,也就是Class = Type*,可是咱们须要的倒是Class = Type。因此这里得用偏特化萃取一下:
template<typename R, typename Class, typename T> class member<R, Class*, T> : public base<R, Class*, T>
这样获得的Class模板形参就会被编译器决议为Type,而不是Type*了。
另外提一下,在function的3种状况的构造函数是模板成员函数,而不是普通成员函数:
template<typename Class, typename _R, typename _T2> function(_R (Class::*ptr)(_T2)) : ptr_(new member<R, T1, T2>(ptr)) { } template<typename _R, typename _T1, typename _T2> function(_R (*ptr)(_T1, _T2)) : ptr_(new func<R, T1, T2>(ptr)) { } template<typename T> function(const T& obj) : ptr_(new functor<T, R, T1, T2>(obj)) { }
前2种状况,普通函数/函数指针对应的构造函数和成员函数指针对应的构造函数实现为成员模板,主要是为了兼容参数的隐式转换,例如声明一个function的类型为function<int (int, int)> foo,调用的时候却传入两个double类型,foo(1.1, 2.2), double类型隐式转换成了int类型。这样也符合boost:function原本的兼容可转换的调用物这一特性。
而第3种状况的成员模板,是为了获取传入的函数对象/boost::bind的类型,以便在存储在functor的数据成员中,这也是为何functor类的模板参数比其余版本多了一个的缘由。
而后,咱们来测试一下:
int get(int a, int b) { std::cout << a+b << std::endl; return 0; } class Point { public: int get(int a) { std::cout << "Point::get called: a = "<< a << std::endl; return a; } int doit(int a, int b) { std::cout << "Point::doit called: a = "<< a+b << std::endl; return a+b; } }; int main(int argc, char const *argv[]) { function<int (int, int)> foo(get); foo(10.1, 10.3); function<int (Point*, int)> bar(&Point::get); Point point; bar(&point, 30); function<int (int, int)> obj(boost::bind(&Point::doit, &point, _1, _2)); obj(90, 100); }
结果为:
20
Point::get called: a = 30 Point::doit called: a = 190
能够看到,输出的内容正是所指望的结果。
(完)