boost::bind详解

本文深入解析Boost.Bind库的使用方法,包括绑定函数、函数对象、成员函数及成员指针的细节,通过实例展示如何灵活运用占位符、bind的嵌套调用及调用约定,解决常见错误。

使用

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);

  1. 通过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(f, _1, _1)(x); // f(x, x)

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

boost::bind(boost::type(), f, _1, _1)(x);

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

  1. 通过pointers to members使用bind

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

bind(&X::f, args); 等价于bind(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 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)

  1. 使用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 v;

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

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

typedef void (*pf)(int);

std::vector v;

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

其中,apply实现如下:

1 template
2 struct apply
3 {
4 typedef R result_type;
5 template result_type operator()(F & f) const
6 {
7 return f();
8 }
9 …
10 };

示例程序

1 // bind_test.cc
2 #include <boost/config.hpp>
3
4 #if defined(BOOST_MSVC)
5 #pragma warning(disable: 4786) // identifier truncated in debug info
6 #pragma warning(disable: 4710) // function not inlined
7 #pragma warning(disable: 4711) // function selected for automatic inline expansion
8 #pragma warning(disable: 4514) // unreferenced inline removed
9 #endif
10
11 #include <boost/bind.hpp>
12 #include <boost/ref.hpp>
13
14 #if defined(BOOST_MSVC) && (BOOST_MSVC < 1300)
15 #pragma warning(push, 3)
16 #endif
17
18 #include
19
20 #if defined(BOOST_MSVC) && (BOOST_MSVC < 1300)
21 #pragma warning(pop)
22 #endif
23
24 #include <boost/detail/lightweight_test.hpp>
25
26 //
27 long f_0() { return 17041L; }
28 long f_1(long a) { return a; }
29 long f_2(long a, long b) { return a + 10 * b; }
30
31
32 long global_result;
33
34 void fv_0() { global_result = 17041L; }
35 void fv_1(long a) { global_result = a; }
36 void fv_2(long a, long b) { global_result = a + 10 * b; }
37
38 void function_test()
39 {
40 using namespace boost;
41
42 int const i = 1;
43
44 BOOST_TEST( bind(f_0)(i) == 17041L );
45 BOOST_TEST( bind(f_1, _1)(i) == 1L );
46 BOOST_TEST( bind(f_2, _1, 2)(i) == 21L );
47
48 BOOST_TEST( (bind(fv_0)(i), (global_result == 17041L)) );
49 BOOST_TEST( (bind(fv_1, _1)(i), (global_result == 1L)) );
50 BOOST_TEST( (bind(fv_2, _1, 2)(i), (global_result == 21L)) );
51 }
52
53
54 //
55 struct Y
56 {
57 short operator()(short & r) const { return ++r; }
58 int operator()(int a, int b) const { return a + 10 * b; }
59 };
60
61 void function_object_test()
62 {
63 using namespace boost;
64 short i(6);
65
66 BOOST_TEST( bind(Y(), ref(i))() == 7 );
67 BOOST_TEST( bind(type(), Y(), ref(i))() == 8 );
68 }
69
70
71 //
72 struct X
73 {
74 mutable unsigned int hash;
75
76 X(): hash(0) {}
77
78 int f0() { f1(17); return 0; }
79 int g0() const { g1(17); return 0; }
80
81 int f1(int a1) { hash = (hash * 17041 + a1) % 32768; return 0; }
82 int g1(int a1) const { hash = (hash * 17041 + a1 * 2) % 32768; return 0; }
83
84 int f2(int a1, int a2) { f1(a1); f1(a2); return 0; }
85 int g2(int a1, int a2) const { g1(a1); g1(a2); return 0; }
86 };
87
88 void member_function_test()
89 {
90 using namespace boost;
91
92 X x;
93
94 // 0
95 bind(&X::f0, &x)();
96 bind(&X::f0, ref(x))();
97 bind(&X::g0, &x)();
98 bind(&X::g0, x)();
99 bind(&X::g0, ref(x))();
100
101 // 1
102 bind(&X::f1, &x, 1)();
103 bind(&X::f1, ref(x), 1)();
104 bind(&X::g1, &x, 1)();
105 bind(&X::g1, x, 1)();
106 bind(&X::g1, ref(x), 1)();
107
108 // 2
109
110 bind(&X::f2, &x, 1, 2)();
111 bind(&X::f2, ref(x), 1, 2)();
112 bind(&X::g2, &x, 1, 2)();
113 bind(&X::g2, x, 1, 2)();
114 bind(&X::g2, ref(x), 1, 2)();
115 }
116
117 void nested_bind_test()
118 {
119 using namespace boost;
120
121 int const x = 1;
122 int const y = 2;
123
124 BOOST_TEST( bind(f_1, bind(f_1, _1))(x) == 1L );
125 BOOST_TEST( bind(f_1, bind(f_2, _1, _2))(x, y) == 21L );
126 }
127
128 int main()
129 {
130 function_test();
131 function_object_test();
132 member_function_test();
133 nested_bind_test();
134
135 return boost::report_errors();
136 }
137
138 //output
139 No errors detected.

易错点

  1. 参数个数不正确

1 int f(int, int);
2
3 int main()
4 {
5 boost::bind(f, 1); // error, f takes two arguments
6 boost::bind(f, 1, 2); // OK
7 }
8 一个此类错误的变形为:忘记成员函数有一个隐式参数this:
9 struct X
10 {
11 int f(int);
12 }
13
14 int main()
15 {
16 boost::bind(&X::f, 1); // error, X::f takes two arguments
17 boost::bind(&X::f, _1, 1); // OK
18 }

复制代码
2. 函数对象不能被指定参数调用
复制代码

1 int f(int);
2
3 int main()
4 {
5 boost::bind(f, “incompatible”); // OK so far, no call
6 boost::bind(f, “incompatible”)(); // error, “incompatible” is not an int
7 boost::bind(f, _1); // OK
8 boost::bind(f, _1)(“incompatible”); // error, “incompatible” is not an int
9 }

复制代码
3. 访问不存在的参数
View Code
4. bind(f, …)形式和bind(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,那么可能导致难以发现的错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值