1 Boost::bind
在介紹bind以前,咱們先介紹一下STL中的綁定機制。咱們知道在C++標準庫中提供了bind1st,bind2nd
函數綁定器和fun_ptr,mem_fun
等函數適配器用來將函數綁定爲一個函數對象。這些函數綁定器和適配器使用起來比較碼分,須要根據全局函數仍是類的成員函數,是一個參數仍是多個參數等做出不一樣的選擇,甚至有時候STL並不能知足咱們的要求。boost庫中提供的Boost::bind
就是爲了解決這個問題而設計的。css
2 標準庫中函數的綁定
咱們首先看一下關於函數綁定是如何出現的。首先咱們先看一下下面這個例子:html
#include <iostream>
#include <vector>
#include <algorithm>
void print(int i)
{
std::cout << i << std::endl;
}
int main()
{
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(2);
std::for_each(v.begin(), v.end(), print);
}
這個例子很簡單,就是依次遍歷向量中的元素,並將其做爲print
的參數進行調用。在標準庫中std::for_each()
算法的第三個參數只能接受一個參數的函數或者函數對象。若是函數有多個參數,則上面的調用方法就行不通了,對應的解決方案是將函數定義爲爲函數對象,而後重載operator()
的方法,最後利用標準庫中的bind1st
或者bind2nd
來綁定重載運算的第一個參數或者第二個參數。
下面這個例子是將一個二元的相減的函數對象綁定爲一個一元函數對象的例子,使用的是標準庫中的std::bind1st
。 java
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
class sub :public std::binary_function<int, int, void>
{
public:
void operator()(int i, int j) const
{
std::cout << i - j << std::endl;
}
};
int main()
{
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(2);
std::for_each(v.begin(), v.end(), std::bind1st(sub(),10));
}
被綁定的二元函數對象必需要繼承std::binary_function
,關於這個函數的使用能夠參考 stl中std::binary_function的使用 。上面使用的是bind1st
將10綁定到第一個參數上,即讓10減去向量中的元素。若是想讓向量減去10則使用bind2nd
,關於標準庫綁定的其餘用法不是本文的重點這裏就不過多介紹了。咱們來看一下標準庫這個用法的缺點,最明顯的一個缺點就是須要改寫函數爲函數對象,由於在咱們的應用中有些函數是容許咱們進行修改的。另一個缺點就是綁定的參數位置不一樣用的綁定方法也不一樣,達不到形式上的統一。看完下面介紹的boost::bind,你就會發現標準庫的功能簡直是太簡陋了。python
3 bind的工做原理
bind並非一個單獨的類或函數,而是很是龐大的家族,依據綁定的參數的個數和要綁定的調用對象的類型,共有數十種不一樣的形式,編譯器會根據具體的綁定代碼自動肯定要使用的正確的形式,bind的基本形式以下:ios
template<class R,class F> bind(F f);
template<class R,class F,class A1>bind(F f,A1 a1);
namespace
{
boost::arg<1> _1;
boost::arg<2> _2;
...
}
bind 接收的 第一個參數 必須是一個 可調用的對象f,包括 函數、函數指針、函數對象和成員函數 ,以後bind 最多接收9個參數,參數數量 必須與 f的參數數量相等 ,這些參數被傳遞給f做爲入參。綁定完成後,bind會 返回一個函數對象 ,它內部 保存了f的拷貝 ,具備 operator(),返回值類型 被自動推到爲 f的返回類型 。在發生調用時這個 函數對象 將把以前 存儲的參數 轉發給f完成調用。例如,有一個函數func,它的形式以下:git
func(a1,a2);
那麼,他將等價於一個具備無參operator()的bind函數對象調用:github
bind(func,a1,a2)();
上面是bind最簡單的調用形式,將一個二元函數綁定爲一個無參的函數對象。事實上,咱們能夠將最多9個參數的函數綁定爲無參函數對象。另外,結合bind的佔位符,能夠實現這些參數以任意順序調用,而不是不用像標準庫那樣指定1st,2nd.下例給出了帶佔位符bind的使用方式:web
bind(func,a1,_1)(a2);
bind(func,_2,_1)(a1,a2);
bind(func._2,_2)(a1,a2);
上面第一個例子中將二元函數綁定爲一個一元函數對象,而且使用了佔位符將一元函數對象的輸入參數傳遞給二元函數的第二個參數。第二個例子和第三個例子分別說明了佔位符能夠出如今綁定函數的任意位置而且能夠重複任意次,只要參數個數同樣便可。能夠看出佔位符的功能很是強大,佔位符的使用也是bind函數的核心功能。下面是使用bind來實現前相減的例子:算法
#include <iostream>
#include <vector>
#include <algorithm>
#include <boost/bind.hpp>
void sub(int a,int b)
{
std::cout<<a-b<<std::endl;
}
int main()
{
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(2);
std::for_each(v.begin(), v.end(), bind(sub,10,_1));
}
能夠看到使用bind以後咱們無需本身去定義函數對象了,能夠保持原有的函數不變,使用bind來將其綁定爲一個一元對象。json
4 bind的擴展使用
4.1 bind綁定成員函數
bind除了能夠將普通的函數綁定爲任意元(小於等於9)的函數對象之外,它能夠對類的成員函數進行綁定。不過在綁定時必需要在第二個參數指定綁定的是類的哪一個對象(以地址的方式傳入)。下面是一個使用示例:
#include <iostream>
#include <string>
#include "boost/bind.hpp"
class TestClass
{
public:
TestClass(int id):Id(id){}
void MemFun(const std::string& str)
{
std::cout<<"Object Id="<<Id<<" \nString="<<str<<std::endl;
}
private:
int Id;
};
int main()
{
TestClass a1(1);
TestClass& a1Ref = a1;
TestClass* a1Ptr = &a1;
TestClass a2(2);
boost::bind(&TestClass::MemFun,&a1,_1)("TestClass string");
boost::bind(&TestClass::MemFun,&a1Ref,_1)("TestClass string");
boost::bind(&TestClass::MemFun,a1Ptr,_1)("TestClass string");
boost::bind(&TestClass::MemFun,&a2,"TestClass string")();
}
上面綁定成員函數的方式和綁定普通函數幾乎沒有任何區別,只須要多指定被綁定的對象便可。另外因爲靜態方法對於一個類來講是惟一的,因此其綁定方法和普通函數的綁定同樣不用指定類的對象,好比若是上面的MemFun
是靜態方法,咱們直接用boost::bind(&TestClass::MemFun,_1)("string")
調用便可。
4.2 複製綁定和非複製綁定
前面咱們在綁定成員函數時,指定對象用的是傳遞地址的方法。在傳遞地址的過程當中沒有發生複製操做。事實上除了指定對象地址之外咱們還能夠指定對象自己,而此時會發生屢次的賦值操做。爲了說明這一過程狀況下面的實例:
#include <iostream>
#include <string>
#include "boost/bind.hpp"
class TestClass
{
public:
TestClass(int id):Id(id){}
void MemFun(const std::string& str)
{
std::cout<<"Object Id="<<Id<<" \nString="<<str<<std::endl;
}
TestClass(const TestClass& other)
{
std::cout<<"copy"<<std::endl;
Id = other.Id;
}
private:
int Id;
};
int main()
{
TestClass a1(1);
boost::bind(&TestClass::MemFun,&a1,_1)("TestClass string");
}
這個例子和上面那個例子的使用是同樣的,只是爲了方便觀察複製過程,這裏自定義了複製操做。上面程序執行結果爲:
Object Id=1
String=TestClass string
接下來咱們將上面例子最後綁定過程改成boost::bind(&TestClass::MemFun,a1,_1)("TestClass string");
,即傳遞對象自己。執行結果爲:
copy
copy
copy
copy
copy
copy
Object Id=1
String=TestClass string
能夠看到當咱們傳遞對象自己時,對象被複制了6次。咱們也能夠藉助boost::ref()
來傳遞對象的引用,即boost::bind(&TestClass::MemFun,boost::ref(a1),_1)("TestClass string");
這句話的執行結果和傳遞指針是同樣的。boost::ref()
是傳遞引用的意思,對應的還有boost::cref()
用來傳遞const引用。咱們能夠在傳遞對象的引用時使用,下面是另一個使用示例:
#include <iostream>
#include <vector>
#include <boost/bind.hpp>
#include <algorithm>
void add(int i,int j,std::ostream &os)
{
os<<i+j<<std::endl;
}
int main()
{
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
std::_For_each(v.begin(),v.end(),boost::bind(add,10,_1,boost::ref(std::cout)));
}
4.3 bind綁定成員變量
處理綁定public成員函數之外,bind也能夠綁定public成員變量。成員變量的綁定和成員函數是同樣的。成員變量綁定爲函數對象之後,調用該函數對象至關於打印該變量,下面是一個使用示例:
#include <iostream>
#include <map>
#include <string>
#include <boost/bind.hpp>
int main()
{
typedef std::pair<int,std::string> pair_t;
pair_t p(123,"myString");
std::cout<<boost::bind(&pair_t::first,&p)()<<std::endl;
std::cout<<boost::bind(&pair_t::second,&p)()<<std::endl;
}
4.4 bind綁定函數對象
除了綁定普通函數和成員函數之外,bind還能夠綁定函數對象。在綁定函數對象的過程當中,若是函數對象 有內部定義的result_type ,那麼bind能夠自動推到出返回值類型,用法和普通函數一致。但若是函數對象 沒有定義result_type ,則須要經過 模板參數手動指定返回類型 ,下面是一個使用示例:
#include <iostream>
#include <boost/bind.hpp>
#include <functional>
struct myFun
{
int operator ()(int a,int b)
{
return a+b;
}
};
int main()
{
std::cout<<boost::bind(std::greater<int>(),_1,10)(5)<<std::endl;
std::cout<<boost::bind<int>(myFun(),_1,3)(8)<<std::endl;
}
在這個示例中,因爲標準庫中的greater函數已經定義了result_type
,因此咱們可78用不用指定函數的返回類型,注意greater的模板參數和bind的模板參數並非一回事。對於第二個綁定過程,因爲咱們自定義的函數對象並無定義定義result_type
全部必需要手動的在模板中指定函數返回值類型。
4.5 綁定非標準函數
處理上面提到的函數之外,咱們也能夠對非標準函數進行綁定。典型的例子就是C中的可變參數函數printf().下面是其綁定的一個實例:
#include <iostream>
#include <boost/bind.hpp>
int main()
{
boost::bind<int>(printf,"%d+1=%d\n",_1,_2)(6,7);
}
因爲printf的返回類型爲int全部咱們必需要在bind的模板參數中指定返回類型。
參考文章:
關於bind的使用能夠參考:
第3章 函數對象
關於bind和標準庫適配器、綁定器的區別,能夠參考:
Boost::bind使用詳解
綁定成員函數(指針,引用,普通對象,靜態、非靜態成員的綁定)能夠參考:
Boost庫bind接口輕鬆實現類成員函數做爲回調函數
更進一步關於bind的實現原理能夠參考(不建議深刻的瞭解其原理,不必從輪子開始造車):
std和boost的function與bind實現剖析