boost::bind 詳解

使用

boost::bind是標準庫函數std::bind1st和std::bind2nd的一種泛化形式。其能夠支持函數對象、函數、函數指針、成員函數指針,而且綁定任意參數到某個指定值上或者將輸入參數傳入任意位置。ios

1. 經過functions和function pointers使用bind

給定以下函數:app

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 }

能夠綁定全部參數,如:ide

bind(f, 1, 2)等價於f(1, 2); bind(g, 1, 2, 3)等價於g(1, 2, 3);函數

也能夠選擇性地綁定參數,如:this

bind(f, _1, 5)(x)等價於f(x, 5),其中_1是一個佔位符,表示用第一個參數來替換;spa

bind(f, _2, _1)(x, y)等價於f(y, x);命令行

bind(g, _1, 9, _1)(x)等價於g(x, 9, x);debug

bind(g, _3, _3, _3)(x, y, z)等價於g(z, z, z);3d

 

說明:指針

傳入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實現以下:

 1 template<class R> 
 2 struct apply
 3 {
 4     typedef R result_type;
 5     template<class F> result_type operator()(F & f) const
 6     {
 7         return f();
 8     }
 9 ...
10 };
View Code

 

示例程序

  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 <iostream>
 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<short>(Y(), ref(i))() == 7 );
 67     BOOST_TEST( bind(type<short>(), 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. 
View Code

 

易錯點

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 }
View Code

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 }
View Code

3. 訪問不存在的參數

1 佔位符_N須要在調用時從指定的參數表中選擇第N個參數:
2 int f(int);
3 
4 int main()
5 {
6     boost::bind(f, _1);                  // OK
7     boost::bind(f, _1)();                // error, there is no argument number 1
8 }
View Code

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

1 bind(f, a1, a2, ..., aN)會對f自動進行類型識別,f必須是一個函數或者成員函數指針。當f是函數對象時,大多數編譯器將不能工做。
2 bind<R>(f, a1, a2, ..., aN)支持任意類型的函數對象。雖然在有些編譯器上,傳入函數或者成員函數指針也能工做,可是不推薦這麼作。
View Code

5. 綁定一個非標準函數

1 bind(f, a1, a2, ..., aN)形式識別<普通的>C++函數和成員函數指針。若是一個函數使用了不一樣的調用約定或者可變參數列表(如std::printf),那麼bind(f, a1, a2, ..., aN)將不能工做;若是確實須要使用此類非標準函數,那麼bind<R>(f, a1, a2, ..., aN)將能知足這種要求。
View Code

6. 綁定一個重載函數

 1 一般狀況下,bind一個重載函數會致使錯誤,由於沒法肯定到底bind重載函數的哪一個形式:
 2 struct X
 3 {
 4     int& get();
 5     int const& get() const;
 6 };
 7 
 8 int main()
 9 {
10     boost::bind(&X::get, _1);
11 }
12 這種二義性能夠經過將成員函數指針轉換到特定類型來解決:
13 int main()
14 {
15     boost::bind(static_cast< int const& (X::*) () const >(&X::get), _1);
16 }
17 此外,一個更可具可讀性的解決辦法爲引入一個臨時變量:
18 
19 int main()
20 {
21     int const& (X::*get) () const = &X::get;
22     boost::bind(get, _1);
23 }
View Code

7. boost的特定編譯器實現問題

 1 // 7.1 MSVC 6.0編譯器
 2 在函數簽名中不支持const:(移除const就能夠了)
 3 int f(int const);
 4 
 5 int main()
 6 {
 7     boost::bind(f, 1);     // error
 8 }
 9 // 7.2 MSVC 7.0如下編譯器
10 (1) 若是經過using聲明引入boost::bind,如:using boost::bind,那麼bind<R>(f, ...)語法將不能工做。
11 解決辦法爲直接使用限定名boost::bind或者使用using指令:using namespace boost;
12 (2) 一個嵌套的命名爲bind的類模板將隱藏函數模板boost::bind,使得bind<R>(f, ...)語法不能工做。
13 (3) MSVC將可變參數中的省略號看做一種類型,所以,其能夠接受以下形式:
14 bind(printf, "%s\n", _1);
15 可是拒絕正確的形式如:
16 bind<int>(printf, "%s\n", _1);
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,那麼可能致使難以發現的錯誤。

相關文章
相關標籤/搜索