Boost.bind的工作原理和应用场景

bind的工作原理:

bind并不是一个单独的类或函数,而是非常庞大的家族,依据绑定的参数的个数和要绑定的调用对象的类型,总共有数十种不同的形式,编译器会根据具体的绑定代码制动确定要使用的正确的形式,bind的基本形式如下:
template<class R,class F> bind(F f);
template<class R,class F,class A1> bind(F f,A1 a1);
namespace
{
boost::arg<1> _1;
boost::arg<2> _2;
boost::arg<3> _3;
…..                                     //其他6个占位符
};

 bind接收的第一个参数必须是一个可调用的对象f,包括函数函数指针函数对象、和成员函数指针,之后bind最多接受9个参数参数数量必须与f的参数数量相等,这些参数被传递给f作为入参。 绑定完成后,bind返回一个函数对象,它内部保存了f的拷贝,具有operator()返回值类型自动推导f的返回类型。在发生调用时这个函数对象将把之前存储的参数转发给f完成调用。例如,有一个函数func,它的形式是:func(a1, a2); 等价于一个具有无参operator()的bind函数对象调用:bind(func, a1, a2)();

 这是bind最简单的形式,bind表达式存储了func和a1、a2的拷贝,产生了一个临时函数对象。因为func接收两个参数,而a1和a2的拷贝传递给func完成真正的函数调用。

  bind的真正威力在于它的占位符,它们分别定义为_1,_2,_3,一直到 _9,位于一个匿名的名字空间。占位符可以取代bind参数的位置,在发生调用时接受真正的参数占位符的名字表示它在调用式中的,而在绑定的表达式中没有没有顺序的要求,_1不一定必须第一个出现,也不一定只出现一次。


应用场景

绑定普通函数

int f(int a,int b){return a+b;}   //二元函数
int g(int a,int b,int c) {return a+b+c;} //三元函数
typedef int (*f_type)(int,int);      //函数指针定义
typedef int (*g_type)(int,int,int); //函数指针定义

cout<<bind(f,1,2)()<<endl;//相当于cout<<f(1,2)<<endl;
cout<<bind(g,1,2,3)()<<endl;//相当于cout<<g(1,2,3)<<endl; 



bind(f,_1,9)(x);//f(x,9)
bind(f,_1,_2)(x,y); //f(x,y)
bind(f,_2,_1)(x,y); //f(y,x)
bind(f,_1,_1)(x,y); //f(x,x),y参数被忽略
bind(g,_1,8,_2)(x,y) //g(x,8,y)
bind(g,_3,_2_2)(x,y,z) //g(z,y,y),x参数被忽略

绑定成员函数

类的成员函数不同于普通的函数,因为成员函数指针不能直接调用operator(),它必须绑定到一个对象指针,然后才能得到this指针进而调用成员函数。因此bind需要 “牺牲”一个占位符,要求提供一个类的实例引用或者指针,通过对象作为第一个参数来调用成员函数

struct demo{int func(int a,int b){return a+b;}};

demo obj;//类的实例对象
demo &ref = obj;//实例对象的引用
demo *ptr = &ref;//实例对象的指针

//bind(&Class::mem_func,obj,_1,_2,…);
cout << bind(&demo::func, obj, _1, 20)(10) << endl;
cout << bind(&demo::func, ref, _2, _1)(20, 10) << endl;
cout << bind(&demo::func, ptr, _1, _2)(10, 20) << endl;

绑定函数对象

bind不仅能够绑定函数和函数指针,也能够绑定任意的函数对象,包括标准库中预定义的函数对象。如果函数对象有内部类型定义result_type,那么bind可以自动推导返回值类型,用法与普通函数一样。但如果函数对象没有定义result_type,则需要在绑定形式上做一点改动,用模板参数指明返回类型,像这样:bind<result_type>(Functor,…);

//标准库和Boost库中的大部分函数都具有result_type定义,因此不需要特别的形式就可以直接使用bind,例如:
bind(std::greater<int>(),_1,10);  //检查 x>10
bind(plus<int>(),_1,_2); //执行 x+y
bind(modulus<int>(),_1,3), //执行 x%3

//对于自定义的函数对象,如果没有result_type类型定义,例如:
struct funcobj{int operator() (int a,int b) {return a +b;}};
bind<int>(funcobj(),_1,_2)(10,20)<<endl;//那么我们必须指明

这种写法所烧会有些不方便,因此,在编写自己的函数对象时,最好遵循规范为它们增加内部typedef result_type,这将使函数对象与其他的标准库和Boost库组件配合工作。

使用ref库

bind采用拷贝的方式保存绑定对象参数,这意味着绑定表达式中的每一个变量都会有一份拷贝,如果函数对象或值参数很大、拷贝代价很高,或者无法拷贝,那么bind的使用就会受到限制。因此bind库可以搭配ref库使用,ref库包装了对象的引用,可以让bind存储对象引用的拷贝,从而降低了拷贝的代价。但这也带来了一个隐患,因为有时候bind的调用可能会延后很久,程序员必须保证bind被调用时引用是有效的。如果调用是引用的变量或者函数对象你被销毁了,那么将会发生未定义行为。ref配合bind用法的代码如下:

int x = 10;
cout<<bind(g,_1,cref(x),ref(x))(10)<<endl;
funcobj fo;
cout<<bind<int>(ref(fo),_1,_2)(10,20)<<endl;

下面的代码则因为引用失效,引发未定义行为:

int x = 10;
BOOST_AUTO(r,ref(x));
{
    int * y = new int(20);
    r = ref(*y);
    cout<<r<<endl;
    cout<<bind(g,r,1,1)()<<endl;
    delete y;
}
cout<<bind(g,r,1,1)()<<endl;


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值