boost::bind是標準庫函數std::bind1st和std::bind2nd的一種泛化形式。其能夠支持函數對象、函數、函數指針、成員函數指針,而且綁定任意參數到某個指定值上或者將輸入參數傳入任意位置。ios
給定以下函數: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);
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::ref和boost::cref來傳入函數對象的引用,尤爲是當該function object是non-copyable或者expensive to copy。
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)
如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 };
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.
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 }
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 }
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 }
1 bind(f, a1, a2, ..., aN)會對f自動進行類型識別,f必須是一個函數或者成員函數指針。當f是函數對象時,大多數編譯器將不能工做。 2 bind<R>(f, a1, a2, ..., aN)支持任意類型的函數對象。雖然在有些編譯器上,傳入函數或者成員函數指針也能工做,可是不推薦這麼作。
1 bind(f, a1, a2, ..., aN)形式識別<普通的>C++函數和成員函數指針。若是一個函數使用了不一樣的調用約定或者可變參數列表(如std::printf),那麼bind(f, a1, a2, ..., aN)將不能工做;若是確實須要使用此類非標準函數,那麼bind<R>(f, a1, a2, ..., aN)將能知足這種要求。
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 }
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);
根據調用約定的不一樣,不一樣的平臺可能支持幾種類型的(成員)函數。例如:
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,那麼可能致使難以發現的錯誤。