bind是c++98标准库中函数适配器bind1st/bind2nd的泛化和增强,可以适配任意的可调用类型,包括函数指针、函数引用、成员函数指针和函数对象。

1、工作原理

bind并不是一个单独的类或函数,而是非常庞大的家族,依据绑定的参数个数和要绑定的调用对象类型,总共有数十个不同的形式,但它们的名字都叫做bind,编译器会根据具体的绑定代码自动确定要使用的正确形式。

bind接受的第一个参数必须是一个科调用对象f,包括函数指针、函数引用、成员函数指针和函数对象,之后bind接受最多九个参数,参数的数量必须与f的参数数量相等,这些参数将被传递给f作为形参。

绑定完成后,bind会返回一个函数对象,它内部保存了f的拷贝,具有operator(),返回值类型被自动推导为f的返回值类型。在发生调用时,这个函数对象将把之前存储的参数转发给f完成调用:

#include "stdafx.h"
#include "boost/utility/result_of.hpp"
#include "boost/typeof/typeof.hpp"
#include "boost/assign.hpp"
#include "boost/ref.hpp"
#include "boost/bind.hpp"
#include "iostream"
using namespace std; 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); int _tmain(int argc, _TCHAR* argv[])
{
cout << boost::bind(f, 1, 2)() << endl;
cout << boost::bind(g, 1, 2, 3)() << endl; return 0;
}

上面的例子是bind最简单的形式。bind表达式存储了func和a1、a2的拷贝,产生了一个临时函数对象。

因为func接受两个参数,而a1和a2都是实参,因此临时函数对象将具有一个午餐的operator()。当operator()调用发生函数对象把a1、a2的拷贝传递给func,完成真正的函数调用。

bind的真正威力在于它的占位符,它们分别被定义为_1、_2、_3一直到_9,位于一个匿名名字空间中。

2、绑定普通函数、绑定函数,用法相同:

#include "stdafx.h"
#include "boost/utility/result_of.hpp"
#include "boost/typeof/typeof.hpp"
#include "boost/assign.hpp"
#include "boost/ref.hpp"
#include "boost/bind.hpp"
#include "iostream"
using namespace std; 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); int _tmain(int argc, _TCHAR* argv[])
{
cout << boost::bind(f, 1, 2)() << endl;
cout << boost::bind(g, 1, 2, 3)() << endl; // 使用占位符
int x = 1, y = 2, z = 3;
cout << boost::bind(f, _1, 2)(x) << endl;
cout << boost::bind(f, _1, _2)(x, y) << endl;
cout << boost::bind(g, _1, 2, 3)(x) << endl;
cout << boost::bind(g, _1, _2, 3)(x, y) << endl;
cout << boost::bind(g, _1, _2, _3)(x, y, z) << endl; // 重点看这个 _2的位置是按照后面参数来匹配的2号位置也就是y的位置
cout << boost::bind(g, x, _2, z)(x, y) << endl;
cout << boost::bind(g, x, _3, z)(z, x, y) << endl; return 0;
}

3、绑定成员函数

类的成员函数不同于普通函数,因为成员函数指针不能直接使用operator(),它必须被绑定到一个对象或指针,然后才能得到this指针进而调用成员函数。

因此bind需要“牺牲”一个占位符的位置,要求用户提供一个类的实例、引用或者指针,通过对象作为第一个参数来调用成员函数:

#include "stdafx.h"
#include "boost/utility/result_of.hpp"
#include "boost/typeof/typeof.hpp"
#include "boost/assign.hpp"
#include "boost/ref.hpp"
#include "boost/bind.hpp"
#include "iostream"
using namespace std; struct demo
{
int f(int a, int b)
{
return (a + b);
}
}; int _tmain(int argc, _TCHAR* argv[])
{
demo a, &ra = a;
demo *p = &a; int x = 1, y = 2;
cout << boost::bind(&demo::f, a, _1, _2)(x, y) << endl;
cout << boost::bind(&demo::f, &ra, _1, _2)(x, y) << endl;
cout << boost::bind(&demo::f, *p, _1, _2)(x, y) << endl; return 0;
}

注意:我们必须在成员函数前加上取地址操作符&,表明这是一个成员函数指针,否则会无法通过编译,这是与绑定函数的一个小小的不同。

bind能够绑定成员函数,这是个非常有用的功能,它可以替代标准库中令人迷惑的mem_fun和mem_fun_ref绑定器,用来配合标准算法操作容器中的对象。

下面的代码使用bind搭配标准算法for_each用来调用容器中所有对象的Print函数:

#include "stdafx.h"
#include "boost/utility/result_of.hpp"
#include "boost/typeof/typeof.hpp"
#include "boost/assign.hpp"
#include "boost/ref.hpp"
#include "boost/bind.hpp"
#include "iostream"
using namespace std;
#include "vector" struct point
{
int x, y;
point(int a = 9, int b = 9) : x(a), y(b)
{ } void Print()
{
cout << "x value is : " << x << " y value is : " << y << endl;
}
}; int _tmain(int argc, _TCHAR* argv[])
{
vector<point> v(10); for_each(v.begin(), v.end(), bind(&point::Print, _1)); return 0;
}

bind同样支持帮顶虚拟成员函数,用法与非虚拟函数相同,虚函数的行为将由实际调用发生时的实例来决定。

4、绑定成员变量

bind的另一个对类的操作是他可以绑定public成员变量,就像一个选择器,用法与绑定成员函数类似,只需要把成员变量名像一个成员函数一样去使用:

#include "stdafx.h"
#include "boost/utility/result_of.hpp"
#include "boost/typeof/typeof.hpp"
#include "boost/assign.hpp"
#include "boost/ref.hpp"
#include "boost/bind.hpp"
#include "iostream"
using namespace std;
#include "string" struct point
{
int x, y;
point(int a = 9, int b = 9) : x(a), y(b)
{ } void Print()
{
cout << "x value is : " << x << " y value is : " << y << endl;
}
}; int _tmain(int argc, _TCHAR* argv[])
{
point pt(1, 99);
typedef pair<int , string> pait_t;
pait_t p(886, "zengraoli"); cout << boost::bind(&point::y, pt)() << endl;
cout << boost::bind(&pait_t::first, p)() << endl;
cout << boost::bind(&pait_t::second, p)() << endl; return 0;
}

5、绑定函数对象

如果函数对象有内部定义result_type,那么bind可以自动推导出返回值类型,用法与绑定普通函数一样。但如果函数对象没有定义result_type,则需要在绑定形式上做一点改动,用模板参数指明返回值类型,标准库和Boost库中的大部分函数对象都具有result_type定义,因此不需要特别的形式就可以直接使用bind:

#include "stdafx.h"
#include "boost/utility/result_of.hpp"
#include "boost/typeof/typeof.hpp"
#include "boost/assign.hpp"
#include "boost/ref.hpp"
#include "boost/bind.hpp"
#include "iostream"
using namespace std;
#include <functional> struct f
{
int operator()(int a, int b)
{
return (a + b);
}
}; int _tmain(int argc, _TCHAR* argv[])
{
cout << boost::bind(plus<int>(), _1, _2)(10, 20) << endl;; // 执行x + y
cout << boost::bind<int>(f(), _1, _2)(10, 20) << endl; return 0;
}

这样的写法boost::bind<int>(f(),_1,_2)(10,20)多少有不方便,因此,在编写自己的函数对象时,最后遵循规范为他们增加内部typedef result_type,这将使函数对象与许多其他标准库和Boost库组件良好配合工作。

6、使用ref库

bind采用拷贝的方式存储绑定对象和参数,这意味着绑定表达式中的每个变量都会有一份拷贝,如果函数对象或值参数很大、拷贝代价很高,或者无法拷贝,那么bind的使用就会受到限制。

因此bind库可以搭配ref库使用,ref库包装了对象的引用,可以让bind存储对象引用的拷贝,从而降低了拷贝的代价。但这也带来了一个隐患,因为有时候bind的调用可能会延后很久,程序员必须保证bind被调用时引用是有效的。如果调用时引用的变量或者函数对象被销毁了,那么会发生未定义行为。

#include "stdafx.h"
#include "boost/utility/result_of.hpp"
#include "boost/typeof/typeof.hpp"
#include "boost/assign.hpp"
#include "boost/ref.hpp"
#include "boost/bind.hpp"
#include "iostream"
using namespace std; int g(int a, int b, int c)
{
return (a + b + c);
} struct f
{
int operator()(int a, int b)
{
return (a + b);
}
}; int _tmain(int argc, _TCHAR* argv[])
{
int x = 10;
cout << boost::bind(g, _1, boost::cref(x), boost::ref(x))(10) << endl; // 因为重载了operator() 所以目前f为一个函数对象
f af;
cout << boost::bind<int>(boost::ref(af), _1, _2)(10, 20) << endl; {
BOOST_AUTO(r, boost::ref(x));
int *y = new int(99);
r = boost::ref(*y);
cout << r << endl; cout << boost::bind(g, r, 1, 1)() << endl;
if (y)
{
delete y;
}
// 下面的代码 因为引用失效,引发了未定义的行为
cout << boost::bind(g, r, 1, 1)() << endl;
} return 0;
}

7、高级议题

为占位符更名:

#include "stdafx.h"
#include "boost/utility/result_of.hpp"
#include "boost/typeof/typeof.hpp"
#include "boost/assign.hpp"
#include "boost/ref.hpp"
#include "boost/bind.hpp"
#include "iostream"
using namespace std; struct f
{
int operator()(int a, int b)
{
return (a + b);
}
}; int _tmain(int argc, _TCHAR* argv[])
{
boost::arg<1> &_x = _1;
boost::arg<2> &_y = _2; f af;
cout << boost::bind<int>(boost::ref(af), _x, _y)(10, 20) << endl; return 0;
}

定义别名也可以使用BOOST_AUTO,这样就无需关心占位符的真实类型,把类型推导的工作交给编译器,有利于编写可移植的代码:

#include "stdafx.h"
#include "boost/utility/result_of.hpp"
#include "boost/typeof/typeof.hpp"
#include "boost/assign.hpp"
#include "boost/ref.hpp"
#include "boost/bind.hpp"
#include "iostream"
using namespace std; struct f
{
int operator()(int a, int b)
{
return (a + b);
}
}; int _tmain(int argc, _TCHAR* argv[])
{
BOOST_AUTO(&_x, _1);
BOOST_AUTO(&_y, _2); f af;
cout << boost::bind<int>(boost::ref(af), _x, _y)(10, 20) << endl; return 0;
}

嵌套绑定

bind可以嵌套,一个bind表达式生成的函数对象可以被另一个bind再绑定,从而实现类似f(g(x))的形式:

#include "stdafx.h"
#include "boost/utility/result_of.hpp"
#include "boost/typeof/typeof.hpp"
#include "boost/assign.hpp"
#include "boost/ref.hpp"
#include "boost/bind.hpp"
#include "iostream"
using namespace std; int g(int a, int b)
{
return (a + b);
} int f(int a, int b)
{
return (a + b);
} int _tmain(int argc, _TCHAR* argv[])
{
int x = 10;
int y = 20;
cout << boost::bind(f, boost::bind(g, _1, _2), _2)(x, y) << endl; return 0;
}

使用bind的嵌套用法必须小心,它不太容易一次写正确,也不太容易理解,超过两个以上的bind表达式通常只能被编译器读懂。

boost------bind的使用(Boost程序库完全开发指南)读书笔记的更多相关文章

  1. boost------signals2的使用2(Boost程序库完全开发指南)读书笔记

    1.应用于观察者模式 本小节将使用signals2开发一个完整的观察者模式示例程序,用来演示信号/插槽的用法.这个程序将模拟一个日常生活场景:客人按门铃,门铃响,护士开门,婴儿哭闹. Ring.h: ...

  2. boost------function的使用(Boost程序库完全开发指南)读书笔记

    function是一个函数对象的“容器”,概念上像是c/c++中函数指针类型的泛化,是一种“智能函数指针”.它以对象的形式封装了原始的函数指针或函数对象,能够容纳任意符合函数签名的可调用对象. 因此, ...

  3. boost------asio库的使用1(Boost程序库完全开发指南)读书笔记

    asio库基于操作系统提供的异步机制,采用前摄器设计模式(Proactor)实现了可移植的异步(或者同步)IO操作,而且并不要求多线程和锁定,有效地避免了多线程编程带来的诸多有害副作用. 目前asio ...

  4. boost------asio库的使用2(Boost程序库完全开发指南)读书笔记

    网络通信 asio库支持TCP.UDP.ICMP通信协议,它在名字空间boost::asio::ip里提供了大量的网络通信方面的函数和类,很好地封装了原始的Berkeley Socket Api,展现 ...

  5. boost------signals2的使用1(Boost程序库完全开发指南)读书笔记

    signals2基于Boost的另一个库signals,实现了线程安全的观察者模式.在signals2库中,观察者模式被称为信号/插槽(signals and slots),他是一种函数回调机制,一个 ...

  6. [转] boost------ref的使用(Boost程序库完全开发指南)读书笔记

    http://blog.csdn.net/zengraoli/article/details/9663057 STL和Boost中的算法和函数大量使用了函数对象作为判断式或谓词参数,而这些参数都是传值 ...

  7. boost------ref的使用(Boost程序库完全开发指南)读书笔记

    STL和Boost中的算法和函数大量使用了函数对象作为判断式或谓词参数,而这些参数都是传值语义,算法或函数在内部保修函数对象的拷贝并使用,例如: #include "stdafx.h&quo ...

  8. Ngine X 完全开发指南 读书笔记-前言

    一开始接触的编程语言是VF,那是一种可视化编程语言,所谓的可视化,就是运行结果能直接看得到的,非常直观,便于调试,适合刚刚接触编程的新人学习.当时学得懵懂,半知半解,就是感觉程序非常神奇,常常几句代码 ...

  9. node.js开发指南读书笔记(1)

    3.1 开始使用Node.js编程 3.1.1 Hello World 将以下源代码保存到helloworld.js文件中 console.log('Hello World!'); console.l ...

随机推荐

  1. nginx 跨域。。。掉坑里了,小心

    今天公司产品一个功能突然挂掉了...向客户演示之前出现了,手机端显示不能获取下载资源,可是急坏了一票人.. 通过手机端,调查服务器地址调用了http:/2342342.domain.hostname. ...

  2. javascript——浅谈javascript模版(自定义)

    /** * Created by Administrator on 15-1-19. */ function functionUtil() { } functionUtil = { //某个DOM节点 ...

  3. 『重构--改善既有代码的设计』读书笔记----Remove Middle Man

    如果你发现某个类做了过多的简单委托动作,你就可以考虑是否可以让客户直接去调用受托类.在Hide Delegate中,我们介绍了封装受托对象的好处,但好处归好处也存在代价,就是当你每次需要在受托对象中增 ...

  4. Ubuntu14.04+CUDA6.5环境下神经网络工具包Deepnet配置

    deepnet是多伦多大学计算机系机器学习组开发的一个神经网络工具包,可以进行以下计算: 1.  Feed-forward Neural Nets 2.  Restricted Boltzmann M ...

  5. sass编译css(转自阮一峰)

    一.什么是SASS SASS是一种CSS的开发工具,提供了许多便利的写法,大大节省了设计者的时间,使得CSS的开发,变得简单和可维护. 本文总结了SASS的主要用法.我的目标是,有了这篇文章,日常的一 ...

  6. meta标签常用属性整理

    在segmentfault看到这篇文章,觉得整理的很详细,所以转载过来和大家分享一下. 原文地址:http://segmentfault.com/blog/ciaocc/119000000240791 ...

  7. Android LinearLayout中weight属性的意义与使用方式

    layout_weight 分割父级容器的比例

  8. BZOJ 1027 合金

    Description 某公司加工一种由铁.铝.锡组成的合金.他们的工作很简单.首先进口一些铁铝锡合金原材料,不同种类的原材料中铁铝锡的比重不同.然后,将每种原材料取出一定量,经过融解.混合,得到新的 ...

  9. GPIO模拟串口注意是事项

    GPIO模拟串口需要注意的事项如下:(程序见我的博客第一篇) 1.由于串口是异步通信,则串口发送必须满足宽度要求. (1)假设串口的波特率是9600bps(1s传输9600个bit),则传输1bit需 ...

  10. Lucky and Good Months by Gregorian Calendar(模拟)

    http://poj.org/problem?id=3393 好大的一道模拟题,直接当阅读理解看了.下面是大神写的题意,解释的好详细. 定义: Goog month : 该月第一个工作日为星期一的月份 ...