boost中bind的使用

最近对boost的bind部分比较感兴趣,对其背后的机制进行了简单的分析,和大家分享一下。


注,我所看的代码是boost_1_64_0, 想来各个版本的差异不大。

定义函数

[cpp] view plaincopy
  1. int f(int a, int b)  
  2. {  
  3.     return a + b;  
  4. }  
  5.   
  6. int g(int a, int b, int c)  
  7. {  
  8.     return a + b + c;  
  9. }  
调用范例:

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
  1. boost::arg<1> _1;  
  2. boost::arg<2> _2;  
  3. boost::arg<3> _3;  
  4. boost::arg<4> _4;  
  5. boost::arg<5> _5;  
  6. boost::arg<6> _6;  
  7. boost::arg<7> _7;  
  8. boost::arg<8> _8;  
  9. boost::arg<9> _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
  1. // bind  
  2.   
  3. #ifndef BOOST_BIND  
  4. #define BOOST_BIND bind  
  5. #endif  
  6.   
  7. // generic function objects  
  8.   
  9. template<class R, class F>  
  10.     _bi::bind_t<R, F, _bi::list0>  
  11.     BOOST_BIND(F f)  
  12. {  
  13.     typedef _bi::list0 list_type;  
  14.     return _bi::bind_t<R, F, list_type> (f, list_type());  
  15. }  
  16.   
  17. template<class R, class F, class A1>  
  18.     _bi::bind_t<R, F, typename _bi::list_av_1<A1>::type>  
  19.     BOOST_BIND(F f, A1 a1)  
  20. {  
  21.     typedef typename _bi::list_av_1<A1>::type list_type;  
  22.     return _bi::bind_t<R, F, list_type> (f, list_type(a1));  
  23. }  
  24.   
  25.   
  26. template<class R, class F, class A1, class A2>  
  27.     _bi::bind_t<R, F, typename _bi::list_av_2<A1, A2>::type>  
  28.     BOOST_BIND(F f, A1 a1, A2 a2)  
  29. {  
  30.     typedef typename _bi::list_av_2<A1, A2>::type list_type;  
  31.     return _bi::bind_t<R, F, list_type> (f, list_type(a1, a2));  
  32. }  
  33. ....  

太多了,只贴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
  1. template<class A1, class A2> struct list_av_2  
  2. {     
  3.     typedef typename add_value<A1>::type B1;  
  4.     typedef typename add_value<A2>::type B2;  
  5.     typedef list2<B1, B2> type;  
  6. };    
list_av_2的作用,仅仅是为了包装而已。list_av_2::type == ist2<A1,A2> == list_type。

bind_t是什么东东?

奥秘在bind_t中,且看bind_t的定义 (也在boost/bind/bind.hpp中。下面只是bind_t的一种定义方法,但是道理都是一样的)
[html] view plaincopy
  1. template<class R, class F, class L> class bind_t  
  2. {  
  3. public:  
  4.   
  5.     typedef bind_t this_type;  
  6.   
  7.     bind_t(F f, L const & l): f_(f), l_(l) {}  
  8.       
  9. #define BOOST_BIND_RETURN return  
  10. #include <boost/bind/bind_template.hpp>  
  11. #undef BOOST_BIND_RETURN  
  12.       
  13. };  
模板参数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
  1. .....  
  2.     template<class A1> result_type operator()(A1 & a1)  
  3.     {  
  4.         list1<A1 &> a(a1);  
  5.         BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0);  
  6.     }  
  7. ....  
  8.   
  9.     template<class A1, class A2> result_type operator()(A1 & a1, A2 & a2)  
  10.     {  
  11.         list2<A1 &, A2 &> a(a1, a2);  
  12.         BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0);  
  13.     }  
  14. ......  

f_就是函数指针,这个不用多说;l_是 L (listN)对象。 

请注意,这里有两个listN出现:
  1. 一个是l_ 这是针对f_提供的参数列表,其中包含_1,_2,....这样的placeholder
  2. 一个是生成的临时变量 a, 这个是bind_t函数对象在调用时 的参数列表
之所以一直强调两个listN,是因为,奥秘就在listN这些个类中的。大家请记住这一点:一直存在两个listN对象

listN的奥秘

bind_t对象的operator () 调用的是listN 的operator (),那么,整个实现,就在listN中,为了方便说明,我们以list2为例。

对list2的分析,我们只看3部分:
1. 类声明部分
[cpp] view plaincopy
  1. templateclass A1, class A2 > class list2: private storage2< A1, A2 >  
  2. {  
  3. private:  
  4.   
  5.     typedef storage2< A1, A2 > base_type;  
  6.   
  7. public:  
  8.   
  9.     list2( A1 a1, A2 a2 ): base_type( a1, a2 ) {}  
从这个定义,我们知道,它从storage2继承,storage2是什么?
[cpp] view plaincopy
  1. template<class A1, class A2> struct storage2: public storage1<A1>  
  2. {  
  3.     typedef storage1<A1> inherited;  
  4.   
  5.     storage2( A1 a1, A2 a2 ): storage1<A1>( a1 ), a2_( a2 ) {}  
  6.   
  7.     template<class V> void accept(V & v) const  
  8.     {     
  9.         inherited::accept(v);  
  10.         BOOST_BIND_VISIT_EACH(v, a2_, 0);   
  11.     }     
  12.   
  13.     A2 a2_;  
  14. };  
从名字和定义上,我们就可以断定:storage2就是保存两个参数的参数列表对象。看来,storageN负责存储,而listN负责如何使用这些参数了。

2. operator[] 系列重载函数
[cpp] view plaincopy
  1.     A1 operator[] (boost::arg<1>) const { return base_type::a1_; }  
  2.     A2 operator[] (boost::arg<2>) const { return base_type::a2_; }  
  3.   
  4.    .....  
  5.   
  6.     template<class T> T & operator[] (_bi::value<T> & v) const { return v.get(); }  
  7. .....  

我已经剔除了一些定义,只留下我们关系的定义。

这里面有两类定义,
  1. 针对 boost::arg<1>和boost::arg<2>定义的。其实就是针对_1, _2的定义,这个定义表明:如果是_1,那么,list2就返回存储的参数a1, 如果是_2,那么就返回存储的参数a2。这些参数,是上面我说的针对f_函数的参数;
  2. 针对_bi::value<T>的定义。_bi::value<T>就是对T进行简单封装的类型。这个定义仅仅是将value的值再取出来。
这两类定义,就是关键所在了。

3. operator()系列重载函数
[html] view plaincopy
  1. ....  
  2.     template<class F, class A> void operator()(type<void>, F & f, A & a, int)  
  3.     {  
  4.         unwrapper<F>::unwrap(f, 0)(a[base_type::a1_], a[base_type::a2_]);  
  5.     }  
  6. ....  

尽管有多种不同的重载,但是基本形式就是这样的。
最后一个参数"int"我想没有直接的意义,可能是为了重载时用于区分不同的重载函数使用的。

unwrap的作用这里可以忽略。你可以认为就是直接调用f。

下面有两个不同寻常的语句:
[html] view plaincopy
  1. a[base_type::a1_], a[base_type::a2_]  
a是一个listN对象,这两句究竟是什么意思呢?

下面,我们用两个例子分别说明,
例子1
bind(f, 1, 2)();                  //f(1,2)
下面,我们将参数代入:这种情况下,我们得到的bind_t对象,实际上是
[cpp] view plaincopy
  1. bind_t<intint(*)(int,int), list2<int,int> >   
  2. //l_的类型和值  
  3. list2<int,int> = [ base_type::a1_ = 1, base_type::a2_ = 2]  
而bind(f, 1, 2) (); 中最后一个括号,调用了bind_t的operator () (void) : 
[cpp] view plaincopy
  1. result_type operator()()  
  2. {  
  3.     list0 a;  
  4.     BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0);  
  5. }  
因此,a = list0。
在这种情况下,
[cpp] view plaincopy
  1. a[base_type::a1_]  = =  
  2. a.operator[]((_bi::value<int>) 1)  
  3. == 1;  
  4.  a[base_type::a2_] ==  
  5. a.operator[](_bi::value<int>) 2)  
  6. == 2;  
其实listN里面的operator[](_bi::value<T>)就是打酱油的,目的就是为了在语法上统一。

因此,bind(f, 1, 2) () === f(1,2)的调用。

例子2
bind(f, _2, _1)(x, y);                 // f(y, x)
我们再把参数代入,这是,得到的bind_t对象就是
[html] view plaincopy
  1. bind_t<int, int(*)(int, int), list2<boost::arg<2>, boost::arg<1> > >  
  2. //l_的类型和值是  
  3. list2<boost::arg<2>,boost::arg<1> > = [ base_type::a1_ = _2base_type::a2_ = _1]  
哈哈,看到了吧,list2的类型不一定要和F的参数类型一样的哦。

当调用bind(f, _2, _1)(x, y); 中bind_t::operator()(int, int) 的时候,即
[cpp] view plaincopy
  1. template<class A1, class A2> result_type operator()(A1 & a1, A2 & a2)  
  2. {  
  3.     list2<A1 &, A2 &> a(a1, a2);  
  4.     BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0);  
  5. }  
此时,a的类型和值是
[cpp] view plaincopy
  1. list2<intint> = [ base_type::a1_= x, base_type::a2_ = y]  
这种情况下,
[html] view plaincopy
  1. a[l_.base_type::a1_] ==  
  2.   a [ _2 ] ==  
  3.  a.operator[] ( (boost::arg<2>&)_2) ==  
  4.  a.base_type::a2_ ==  
  5. y;  
  6.   
  7. a[l_.base_type::a2_] ==  
  8.   a [ _1 ] ==  
  9.  a.operator[] ( (boost::arg<1>&)_1) ==  
  10.  a.base_type::a1_ ==  
  11. x;  

即 bind(f, _2, _1)(x, y) === f(y, x);

关于_1,_2,_3,...._9

现在,我们要看看,到底 boost::arg<1>, boost::arg<2> ... boost::arg<9>是什么了。
[cpp] view plaincopy
  1. templateint I > struct arg   
  2. {  
  3.     arg()  
  4.     {     
  5.     }     
  6.   
  7.     templateclass T > arg( T const & /* t */ )  
  8.     {     
  9.         // static assert I == is_placeholder<T>::value  
  10.         typedef char T_must_be_placeholder[ I == is_placeholder<T>::value? 1: -1 ];  
  11.     }     
  12. };  

呵呵,什么都没有!的确,什么都没有,因为它是placheholder嘛! 它只要表明自己的类型就可以了。这是为什么在 listN的operator[] 中,boost::arg<N> 没有定义形惨了。

第二个带有 T const & 参数的构造函数是什么?看起来很奇怪,其实,它是拷贝构造函数。

看看is_placeholder的定义吧:
[cpp] view plaincopy
  1. templateint I > struct is_placeholder< arg<I> >  
  2. {  
  3.     enum _vt { value = I };  
  4. };  

它的作用,是防止错误的参考构造。
假如,你这样定义:
[cpp] view plaincopy
  1. boost::arg<2> arg2(_1);  
模板展开后,将是这样的
[cpp] view plaincopy
  1. struct arg <2>  
  2. {   
  3. .....  
  4.     arg( arg<1> const & /* t */ )  
  5.     {     
  6.         // static assert I == is_placeholder<T>::value  
  7.         typedef char T_must_be_placeholder[ I == is_placeholder<arg<1> >::value? 1: -1 ];  
  8.     }     
  9. };  
is_placeholder<arg<1> >::value == 1,而I == 2,因此,你会得到一个
[cpp] view plaincopy
  1. typedef char T_must_be_placeholder[ -1 ];  
因此,你将收到一个编译错误。仅此而已。

其他

到此为止,boost bind的关键部分就已经清楚了。boost还有些高级议题,如类的成员函数的绑定、变量引用、绑定嵌套等等。这些议题都是以此为基础,再增加了一些新模板参数而已,已经不是实现的核心了,有兴趣的同学可以自己看看。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/463984.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

这道字符串反转的题目,你能想到更好的方法吗?

周末有一个朋友问了一个笔试题目&#xff0c;当时还直播写了答案&#xff0c;但是总觉得写得不够好&#xff0c;现在把题目放出来。大家看看有没有什么更好的解法题目有一个字符串&#xff0c;如下&#xff0c;要求对字符串做反转后输出//input the sky is blue//output blue …

WinAPI: 钩子回调函数之 SysMsgFilterProc

SysMsgFilterProc(nCode: Integer; {}wParam: WPARAM; {}lParam: LPARAM {} ): LRESULT; {}//待续...转载于:https://www.cnblogs.com/del/archive/2008/02/25/1080722.html

流媒体服务器搭建实例——可实现录音,录像功能

由于我也是刚开始接触这个东东&#xff0c;原理什么的不是很清楚&#xff0c;这里我就不说了&#xff0c;免得误人子弟&#xff0c;嘿嘿&#xff01;第一步&#xff0c;下载FlashMediaServer3.5&#xff0c;网上有很多资源&#xff0c;这里就不提供了&#xff0c;大家google一下…

使用HanLP增强Elasticsearch分词功能

hanlp-ext 插件源码地址&#xff1a;http://git.oschina.net/hualongdata/hanlp-ext 或 https://github.com/hualongdata/hanlp-ext Elasticsearch 默认对中文分词是按“字”进行分词的&#xff0c;这是肯定不能达到我们进行分词搜索的要求的。官方有一个SmartCN 中文分词插件&…

boost::function的用法(二)

boost function是一组类和模板组合&#xff0c;用于包装各种函数。从功能上&#xff0c;它类似于函数指针&#xff0c;但是比函数指针的功能更强大。 使用boost function&#xff0c;必须包含头文件 [cpp] view plaincopy #include <boost/function.hpp> 除了头文件外&…

一个女孩子居然做了十年硬件。​。。

本文转自面包板社区。--正文--2011年&#xff0c;一个三本大学的电子信息专业的大三女学生跟2个通信专业的大二男生组成了一组代表学校参加2011年“瑞萨杯”全国大学生电子设计大赛&#xff0c;很意外的获得了湖北赛区省三等奖&#xff0c;虽然很意外&#xff0c;但还是挺高兴的…

自动备份多个MOSS站点集的脚本

写了个自动备份多个站点集的脚本&#xff08;backupscript.bat&#xff09;可以生成文件名如"Site80_20080227.bak"的备份文件。 echo off echo echo Backup of MOSS site! echo Written by WangWei(shangmeizhaihotmail.com) echo set yyyy%Date:~0,4% set mm%Dat…

Chrome Version 19.0.1055.1 dev Flash Missing plug-in的修复

linux mint12 64位&#xff0c;以前一直用chrome好好的&#xff0c;直到更新到 Version 19.0.1055.1 dev版本&#xff0c;问题一大堆&#xff0c;最纠结的是flash不能用了&#xff0c;更新前是正常的&#xff0c;一直也没有找到原因&#xff0c;通过命令行启动google-chrome发现…

记录上一个项目踩过的坑

1、objecthtmldivelement对象 var avc document.getElementById("div1"); alert(avc ); 为objecthtmldivelement对象&#xff0c;objecthtmldivelement对象相当于document.getElementById("div1")。2、获取img的src function imgsrc(){var y document.g…

之前字符串反转的题目

之前发的字符串反转的题目这道字符串反转的题目&#xff0c;你能想到更好的方法吗&#xff1f;有很多人评论了&#xff0c;有的人还写了自己的解题思路&#xff0c;还有人写了自己的代码还有其中呼声很高的压栈解法我相信很多人在笔试的时候一定会遇到这类题目&#xff0c;给你…

C++ 多重继承之内存存储

C 之多重继承 1. C中class与struct。 在C里面&#xff0c;class与struct没有本质的区别&#xff0c;只是class的默认权限是private&#xff0c;而struct则是public。这个概念也揭示了一点&#xff1a;class和struct在内部存储结构上是一致的。所以我们可以利用这一点来探讨clas…

hdu 3488

可以作为KM 二分图最大权匹配模板 View Code #include <stdio.h>#include <iostream>#include <string.h>using namespace std;const int N210;const int inf0x2fffffff;const int Max20000;int match[N],n,m,lack,w[N][N],lx[N],ly[N];bool vx[N],vy[N];bo…

Find The Multiple POJ - 1426 (BFS)

题目大意 给定一个整数&#xff0c;寻找一个只有0,1构成的十进制数使得这个数能够整除这个整数 解法 直接bfs第一位放入1&#xff0c;之后每一位放入1或者0 代码 #include <iostream> #include <queue> using namespace std; int n; void bfs() {queue<long lon…

心情不好,我就这样写代码

在 GitHub 上有一个项目&#xff0c;它描述了「最佳垃圾代码」的十九条关键准则。从变量命名到注释编写&#xff0c;这些准则将指导你写出最亮眼的烂代码。为了保持与原 GitHub 项目一致的风格&#xff0c;下文没有进行转换。读者们可以以相反的角度来理解所有观点&#xff0c;…

C++中默认构造函数使用时的要点

最近写代码的时候发现一个奇怪的现象&#xff1a;当我声明一个无参构造函数时&#xff0c;如果后面加上括号&#xff0c;声明出的对象就不能显示。比如下面的代码&#xff1a; [cpp] view plaincopy #include <stdio.h> class Test { public: Test() { …

一个立即关闭显示器的小软件(Masm开发,只有3KB大小)

我们在用电脑听歌或者下载网络资源时&#xff0c;经常都是不需要开着显示器的&#xff0c;这样不仅可以省电&#xff0c;最重要的是可以延长显示器的使用寿命&#xff0c;当然&#xff0c;对于笔记本电脑的电池省电也是很重要的。另外&#xff0c;对于液晶显示器&#xff0c;由…

动态规划之91 decode ways

题目链接&#xff1a;https://leetcode-cn.com/problems/decode-ways/description/ 参考&#xff1a;https://www.jianshu.com/p/5a604070cd11 题目大意&#xff1a;将一串数字&#xff0c;编码成A-Z的字符串。因为12-->L&#xff0c;或者12-->AB。所有12转成字符串一共有…

SQL Server中的STUFF函数的使用

STUFF ( character_expression , start , length ,character_expression ) 参数 character_expression 一个字符数据表达式。character_expression 可以是常量、变量&#xff0c;也可以是字符列或二进制数据列。start 一个整数值&#xff0c;指定删除和插入的开始位置。如果 st…

递归是会更秀strtok

前几天发的字符串反转题目&#xff0c;后面有一个新同学用了递归的方法来实现&#xff0c;看了下&#xff0c;真的是很秀。之前字符串反转的题目代码如下#include "stdio.h" #include "string.h" char input[] {"the sky is blue cris 1212321 apple…

判断输入的IP地址是否合法

判断输入的IP地址是否合法&#xff0c;ip地址的值在0~255之间&#xff0c;先把输入的IP的地址转换为一个字符串。 #define LEN (sizeof(xx)/sizeof(xx[0])) const char *xx[] {"192.168.1.1", "10.0.0.1", "127.256.0.1", "iugerjiogjioe…