boost::bind 详解

转自:https://www.cnblogs.com/benxintuzi/p/4862129.html

boost::bind是标准库函数std::bind1st和std::bind2nd的一种泛化形式。其可以支持函数对象、函数、函数指针、成员函数指针,并且绑定任意参数到某个指定值上或者将输入参数传入任意位置。

1. 通过functions和function pointers使用bind

给定如下函数:

复制代码
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(g, 1, 2, 3)等价于g(1, 2, 3);

也可以选择性地绑定参数,如:

bind(f, _1, 5)(x)等价于f(x, 5),其中_1是一个占位符,表示用第一个参数来替换;

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函数的参数一般为变量的copy,如:

int i = 5;

bind(f, i, _1);

如果想传入变量的引用,可以使用boost::ref和boost::cref,如:

int i = 5;

bind(f, ref(i), _1);

bind(f, cref(i), _1);

 

2. 通过function objects使用bind

复制代码
1 struct F
2 {
3     int operator()(int a, int b) { return a – b; }
4     bool operator()(long a, long b) { return a == b; }
5 };
6 
7 F f;
8 int x = 100;
9 bind<int>(f, _1, _1)(x);        // f(x, x)
复制代码

可能某些编译器不支持上述的bind语法,可以用下列方式代替:

boost::bind(boost::type<int>(), f, _1, _1)(x);

默认情况下,bind拥有的是函数对象的副本,但是也可以使用boost::refboost::cref来传入函数对象的引用,尤其是当该function object是non-copyable或者expensive to copy。

 

3. 通过pointers to members使用bind

bind将传入的成员(数据成员和成员函数)指针作为第一个参数,其行为如同使用boost::mem_fn将成员指针转换为一个函数对象,即:

bind(&X::f, args);       等价于bind<R>(mem_fn(&X::f), args),其中R为X::f的返回类型(成员函数)或类型(数据成员)。

复制代码
 1 struct X
 2 {
 3     bool f(int a);
 4 };
 5 
 6 X x;
 7 shared_ptr<X> p(new X);
 8 int i = 5;
 9 
10 bind(&X::f, ref(x), _1)(i);        // x.f(i)
11 bind(&X::f, &x, _1)(i);            // (&x)->f(i)
12 bind(&X::f, x, _1)(i);            // x.f(i)
13 bind(&X::f, p, _1)(i);            // p->f(i)
复制代码

 

4. 使用nested binds

如bind(f, bind(g, _1))(x)中:

在外部bind计算之前,内部bind先被计算(如果内部有多个bind,则计算顺序不定)。如上,根据参数x,先计算bind(g, _1)(x),生成g(x),然后计算bind(f, g(x))(x),最后生成f(g(x))。

 

但是要注意:

bind中的第一个参数不参与计算过程,假设如下程序想要实现对于向量v中的每个函数指针,传入一个参数 5:

typedef void (*pf)(int);

std::vector<pf> v;

std::for_each(v.begin(), v.end(), bind(_1, 5));

上述程序并没有实现我们想要的结果:可以通过使用一个帮助函数对象apply,该对象可以将bind的第一个参数作为一个函数对象,如下:

typedef void (*pf)(int);

std::vector<pf> v;

std::for_each(v.begin(), v.end(), bind(apply<void>(), _1, 5));

其中,apply实现如下:

  View Code

 

示例程序

  View Code

 

易错点

1. 参数个数不正确

  View Code

2. 函数对象不能被指定参数调用

  View Code

3. 访问不存在的参数

  View Code

4. bind(f, ...)形式和bind<R>(f, ...)形式的不当用法

  View Code

5. 绑定一个非标准函数

  View Code

6. 绑定一个重载函数

  View Code

7. boost的特定编译器实现问题

  View Code

 

调用约定

根据调用约定的不同,不同的平台可能支持几种类型的(成员)函数。例如:

Windows API函数和COM接口成员函数使用__stdcall;

Borland VCL使用__fastcall;

Mac toolbox函数使用pascal。

与__stdcall函数一起使用bind时,在包含<boost/bind.hpp>之前#define the macro BOOST_BIND_ENABLE_STDCALL;

与__stdcall成员函数一起使用bind时,在包含<boost/bind.hpp>之前#define the macro BOOST_MEM_FN_ENABLE_STDCALL;

与__fastcall函数一起使用bind时,在包含<boost/bind.hpp>之前#define the macro BOOST_BIND_ENABLE_ FASTCALL;

与__fastcall成员函数一起使用bind时,在包含<boost/bind.hpp>之前#define the macro BOOST_MEM_FN_ENABLE_ FASTCALL;

与pascal函数一起使用bind时,在包含<boost/bind.hpp>之前#define the macro BOOST_BIND_ENABLE_ PASCAL;

与__cdecl成员函数一起使用bind时,在包含<boost/bind.hpp>之前#define the macro BOOST_MEM_FN_ENABLE_CDECL;

一个比较好的建议是:如果需要使用bind,要么提前在工程选项中定义这些宏,要么通过命令行选项-D定义,要么直接在使用bind的.cc文件中定义。否则如果包含bind.hpp的文件中,发生了在定义这些宏之前including bind.hpp,那么可能导致难以发现的错误。

 


猜你喜欢

转载自blog.csdn.net/haima1998/article/details/80548385