做代理的网站,新闻类网站怎么做百度推广,wordpress个人博客模板下载,重庆视频制作公司排名最近对boost的bind部分比较感兴趣#xff0c;对其背后的机制进行了简单的分析#xff0c;和大家分享一下。 注#xff0c;我所看的代码是boost_1_64_0#xff0c; 想来各个版本的差异不大。 定义函数 [cpp] view plaincopy int f(int a, int b) { return a b; }… 最近对boost的bind部分比较感兴趣对其背后的机制进行了简单的分析和大家分享一下。 注我所看的代码是boost_1_64_0 想来各个版本的差异不大。 定义函数 [cpp] view plaincopy int f(int a, int b) { return a b; } int g(int a, int b, int c) { return a b c; } 调用范例 bind(f, 1, 2)(); //f(1,2)bind(f, _2, _1)(x, y); // f(y, x)bind(g, _1, 9, _1)(x); // g(x, 9, x)bind(g, _3, _3, _3)(x, y, z); // g(z, z, z)bind(g, _1, _1, _1)(x, y, z); // g(x, x, x)_1, _2, ... _9在 boost中被称为placeholder是占位符的意思。它表示参数。这种方式我是只在boost中见过是个非常神奇的用法。 它们究竟是什么呢且看定义(boost/bind/placeholders.hpp) [cpp] view plaincopy boost::arg1 _1; boost::arg2 _2; boost::arg3 _3; boost::arg4 _4; boost::arg5 _5; boost::arg6 _6; boost::arg7 _7; boost::arg8 _8; boost::arg9 _9; boost::arg也是个模板至于是什么样的模板留个悬念吧。boost bind的这些功能颠覆了我对C的看法从未想到过C还可以这么玩。那么boost究竟是怎么实现的呢 读者请注意bind在这里涉及了两个参数表。第一个参数表是被bind绑定的函数(例子中f,g函数)的参数表另外一个是bind生成的新的函数对象的参数表。 这两个参数表如何实现如何转换是我们后面分析的重点。 bind是什么 bind是函数是非常神奇的函数不是一个函数而是一组函数是一组重载的函数。 翻开代码 boost/bind/bind.hpp找到BOOST_BIND字符串大约在1290行的位置boost定义了bind(1.51.0) [cpp] view plaincopy // bind #ifndef BOOST_BIND #define BOOST_BIND bind #endif // generic function objects templateclass R, class F _bi::bind_tR, F, _bi::list0 BOOST_BIND(F f) { typedef _bi::list0 list_type; return _bi::bind_tR, F, list_type (f, list_type()); } templateclass R, class F, class A1 _bi::bind_tR, F, typename _bi::list_av_1A1::type BOOST_BIND(F f, A1 a1) { typedef typename _bi::list_av_1A1::type list_type; return _bi::bind_tR, F, list_type (f, list_type(a1)); } templateclass R, class F, class A1, class A2 _bi::bind_tR, F, typename _bi::list_av_2A1, A2::type BOOST_BIND(F f, A1 a1, A2 a2) { typedef typename _bi::list_av_2A1, A2::type list_type; return _bi::bind_tR, F, list_type (f, list_type(a1, a2)); } .... 太多了只贴3个足以说明问题。 模板参数 R 表示返回类型F 表示函数指针的类型A1,A2, .... 这些都是参数的类型
boost将BOOST_BIND定义为bind。所以你看到的BOOST_BIND就是对bind函数的定义。bind函数非常简单它其实就是返回一个bind_t类的对象。bind_t类也是一个模板类。 如果仔细观察你可以发现这些不同参数的bind函数其实现上不同在于list_av_N这一系列的辅助类是不同的。list_av_1表示一个参数list_av_2表示两个参数, 以此类推。这里的list_av_N对象是第一个参数表是函数F要求的参数表这个参数表是可以包含placeholder的。list_av_N对象其实只是一个包装对象。我们以list_av_2为例[cpp] view plaincopy templateclass A1, class A2 struct list_av_2 { typedef typename add_valueA1::type B1; typedef typename add_valueA2::type B2; typedef list2B1, B2 type; }; list_av_2的作用仅仅是为了包装而已。list_av_2::type ist2A1,A2 list_type。 bind_t是什么东东?
奥秘在bind_t中且看bind_t的定义 (也在boost/bind/bind.hpp中。下面只是bind_t的一种定义方法但是道理都是一样的)[html] view plaincopy templateclass R, class F, class L class bind_t { public: typedef bind_t this_type; bind_t(F f, L const l): f_(f), l_(l) {} #define BOOST_BIND_RETURN return #include boost/bind/bind_template.hpp #undef BOOST_BIND_RETURN }; 模板参数R代表return type, F代表function type, L表示的是listN(list0,list1,list2,....)这个是关键啊。至于bind_template.hpp这个源代码也比较简单主要是定义了operator () 的实现。bind_t重载括号运算符因此bind_t可以像函数那样调用。而且bind_t的operator()有N多个重载分别对应的是不同的参数类型和参数个数。这使得我们可以用不同的参数调用bind_t的对象。我们摘抄一个有一两个参数的括号重载看看[cpp] view plaincopy ..... templateclass A1 result_type operator()(A1 a1) { list1A1 a(a1); BOOST_BIND_RETURN l_(typeresult_type(), f_, a, 0); } .... templateclass A1, class A2 result_type operator()(A1 a1, A2 a2) { list2A1 , A2 a(a1, a2); BOOST_BIND_RETURN l_(typeresult_type(), f_, a, 0); } ...... f_就是函数指针这个不用多说l_是 L (listN)对象。 请注意这里有两个listN出现一个是l_ 这是针对f_提供的参数列表其中包含_1,_2,....这样的placeholder一个是生成的临时变量 a, 这个是bind_t函数对象在调用时 的参数列表 之所以一直强调两个listN是因为奥秘就在listN这些个类中的。大家请记住这一点一直存在两个listN对象。 listN的奥秘
bind_t对象的operator () 调用的是listN 的operator ()那么整个实现就在listN中为了方便说明我们以list2为例。对list2的分析我们只看3部分1. 类声明部分[cpp] view plaincopy template class A1, class A2 class list2: private storage2 A1, A2 { private: typedef storage2 A1, A2 base_type; public: list2( A1 a1, A2 a2 ): base_type( a1, a2 ) {} 从这个定义我们知道它从storage2继承storage2是什么[cpp] view plaincopy templateclass A1, class A2 struct storage2: public storage1A1 { typedef storage1A1 inherited; storage2( A1 a1, A2 a2 ): storage1A1( a1 ), a2_( a2 ) {} templateclass V void accept(V v) const { inherited::accept(v); BOOST_BIND_VISIT_EACH(v, a2_, 0); } A2 a2_; }; 从名字和定义上我们就可以断定storage2就是保存两个参数的参数列表对象。看来storageN负责存储而listN负责如何使用这些参数了。2. operator[] 系列重载函数[cpp] view plaincopy A1 operator[] (boost::arg1) const { return base_type::a1_; } A2 operator[] (boost::arg2) const { return base_type::a2_; } ..... templateclass T T operator[] (_bi::valueT v) const { return v.get(); } ..... 我已经剔除了一些定义只留下我们关系的定义。这里面有两类定义针对 boost::arg1和boost::arg2定义的。其实就是针对_1, _2的定义这个定义表明如果是_1,那么list2就返回存储的参数a1, 如果是_2那么就返回存储的参数a2。这些参数是上面我说的针对f_函数的参数针对_bi::valueT的定义。_bi::valueT就是对T进行简单封装的类型。这个定义仅仅是将value的值再取出来。 这两类定义就是关键所在了。3. operator()系列重载函数[html] view plaincopy .... templateclass F, class A void operator()(typevoid, F f, A a, int) { unwrapperF::unwrap(f, 0)(a[base_type::a1_], a[base_type::a2_]); } .... 尽管有多种不同的重载但是基本形式就是这样的。最后一个参数int我想没有直接的意义可能是为了重载时用于区分不同的重载函数使用的。unwrap的作用这里可以忽略。你可以认为就是直接调用f。下面有两个不同寻常的语句 [html] view plaincopy a[base_type::a1_], a[base_type::a2_] a是一个listN对象这两句究竟是什么意思呢下面我们用两个例子分别说明例子1bind(f, 1, 2)(); //f(1,2) 下面我们将参数代入这种情况下我们得到的bind_t对象实际上是[cpp] view plaincopy bind_tint, int(*)(int,int), list2int,int //l_的类型和值 list2int,int [ base_type::a1_ 1, base_type::a2_ 2] 而bind(f, 1, 2) (); 中最后一个括号调用了bind_t的operator () (void) : [cpp] view plaincopy result_type operator()() { list0 a; BOOST_BIND_RETURN l_(typeresult_type(), f_, a, 0); } 因此a list0。在这种情况下[cpp] view plaincopy a[base_type::a1_] a.operator[]((_bi::valueint) 1) 1; a[base_type::a2_] a.operator[](_bi::valueint) 2) 2; 其实listN里面的operator[](_bi::valueT)就是打酱油的目的就是为了在语法上统一。因此bind(f, 1, 2) () f(1,2)的调用。例子2bind(f, _2, _1)(x, y); // f(y, x) 我们再把参数代入这是得到的bind_t对象就是[html] view plaincopy bind_tint, int(*)(int, int), list2boost::arg2, boost::arg1 //l_的类型和值是 list2boost::arg2,boost::arg1 [ base_type::a1_ _2, base_type::a2_ _1] 哈哈看到了吧list2的类型不一定要和F的参数类型一样的哦。当调用bind(f, _2, _1)(x, y); 中bind_t::operator()(int, int) 的时候即[cpp] view plaincopy templateclass A1, class A2 result_type operator()(A1 a1, A2 a2) { list2A1 , A2 a(a1, a2); BOOST_BIND_RETURN l_(typeresult_type(), f_, a, 0); } 此时a的类型和值是[cpp] view plaincopy list2int, int [ base_type::a1_ x, base_type::a2_ y] 这种情况下[html] view plaincopy a[l_.base_type::a1_] a [ _2 ] a.operator[] ( (boost::arg2)_2) a.base_type::a2_ y; a[l_.base_type::a2_] a [ _1 ] a.operator[] ( (boost::arg1)_1) a.base_type::a1_ x; 即 bind(f, _2, _1)(x, y) f(y, x); 关于_1,_2,_3,...._9
现在我们要看看到底 boost::arg1, boost::arg2 ... boost::arg9是什么了。[cpp] view plaincopy template int I struct arg { arg() { } template class T arg( T const /* t */ ) { // static assert I is_placeholderT::value typedef char T_must_be_placeholder[ I is_placeholderT::value? 1: -1 ]; } }; 呵呵什么都没有的确什么都没有因为它是placheholder嘛! 它只要表明自己的类型就可以了。这是为什么在 listN的operator[] 中boost::argN 没有定义形惨了。第二个带有 T const 参数的构造函数是什么看起来很奇怪其实它是拷贝构造函数。看看is_placeholder的定义吧[cpp] view plaincopy template int I struct is_placeholder argI { enum _vt { value I }; }; 它的作用是防止错误的参考构造。假如你这样定义[cpp] view plaincopy boost::arg2 arg2(_1); 模板展开后将是这样的[cpp] view plaincopy struct arg 2 { ..... arg( arg1 const /* t */ ) { // static assert I is_placeholderT::value typedef char T_must_be_placeholder[ I is_placeholderarg1 ::value? 1: -1 ]; } }; is_placeholderarg1 ::value 1而I 2因此你会得到一个 [cpp] view plaincopy typedef char T_must_be_placeholder[ -1 ]; 因此你将收到一个编译错误。仅此而已。其他
到此为止boost bind的关键部分就已经清楚了。boost还有些高级议题如类的成员函数的绑定、变量引用、绑定嵌套等等。这些议题都是以此为基础再增加了一些新模板参数而已已经不是实现的核心了有兴趣的同学可以自己看看。