C++11 學習筆記 std::function和bind綁定器
一.std::function
C++中的可調用對象雖然具有比較統一操作形式(除了類成員指針之外,都是后面加括號進行調用),但定義方法五花八門。為了統一泛化函數對象,函數指針,引用函數,成員函數的指針的各種操作,讓我們可以按更統一的方式寫出更加泛化的代碼,C++11推出了std::function。
std::function是可調用對象的包裝器。它是一個類模板,可以容納除了類成員(函數)指針之外的所有可調用對象。通過指定它的模板參數,它可以用統一的方式處理函數,函數對象,函數指針,並允許保存和延遲執行它們。
#1include <iostream> #include <functional> using namespace std; void func(void){ cout << __FUNCTION__ << "(" << a << ") ->: "; } class Foo { public: static int foo_func(int a){ cout << __FUNCTION__ << "(" << a << ") ->: "; return a; } }; class bar { public: int operator()(int a){ cout << __FUNCTION << "(" << a << ") ->: "; return a; } }; int main(){ //綁定一個普通函數 std::function<void(void)> fry = func; fr1(); //綁定一個類的靜態成員函數 std::function<int(int)> fr2 = Foo::foo_func; cout << fr2(123) << endl; //綁定一個仿函數 Bar bar; fr2 = bar; cout << fr2(123) <<endl; return 0; }
std::function的使用方法:我們給std::function填入合適的函數簽名(即一個函數類型,只需要包括返回值和參數表)之后,它就變成了一個可以容納所有這一類調用方式的“函數包裝器”。
#include <iostream> #include <functional> using namespace std; class A { public: A(const std::function<void()>& f){ :callback_(f){} void notify(void){ callback_(); } private: std::function<void()> callback_; }; class Foo { public: void operator()(void){ cout << __FUNCTION__<< endl; } }; int main(){ Foo foo; A aa(foo); aa.notify(); return 0; }
從上面的例子看,std::function可以取代函數指針的作用。因為它可以保存函數延遲執行,所以比較適合作為回調函數,也可以把它看做類似於C#中特殊的委托(只有一個成員的委托)。
#include <iostream> #include <functional> using namespace std; void call_when_even(int x, const std::function<void(int)>& f){ if(!(x & 1)){ f(x); } } void output(int x){ cout << x <<" "; } int main(void){ for(int i=0;i<10;i++){ call_when_even(i, output); } cout<<endl; return 0; }
std::function還可以作為函數入參,這樣可以在函數外部控制函數的內部行為了,讓我們的函數變得更加靈活。
二.std::bind綁定器
std::bind用來將可調用對象與其參數一起進行綁定。綁定后的結果可以使用std::function進行保存,並延遲調用到任何我們需要的時候。通俗來講,它主要有兩大作用:
1).將可調用對象與其參數一起綁定成一個仿函數。
2).將多元(參數個數為n,n>1)可調用對象轉成一元或者(n-1)元可調用對象,即只綁定部分參數。
function模板類和bind模板函數,都可以實現類似函數指針的功能,但卻卻比函數指針更加靈活,特別是函數指向類的非靜態成員函數時。
1).std::function可以綁定到全局函數/類靜態成員函數(類靜態成員函數與全局函數沒有區別)。
2).綁定到類的非靜態成員函數,則需要使用std::bind。
#include <iostream> #include <functional> using namespace std; void call_when_even(int x, const std::function<void(int)>& f){ if(!(x & 1)){ f(x); } } void output(int x){ cout << x << " "; } void output_add_2(int x){ cout << x +2 << " "; } int main(){ { auto fr = std::bind(output, std::placeholders::_1); for(int i=0;i<10;i++){ call_when_even(i, fr); } cout << endl; } { auto fr = std::bind(output_add_2, std::placeholders::_1); for(int i=0;i<10;i++){ call_when_even(i, fr); } cout << endl; } return 0; }
"std::placeholders::_1"是一個占位符對象,用於表示當函數output(output_add_2)通過函數fr進行調用時,函數fr的第一個參數在函數output(output_add_2)的參數列表中的位置。
下面是兩個樣例:
#include <iostream> #include <functional> using namespace std; class A { public: int i_=0; void output(int x, int y){ cout << x << " " << y <<endl; } }; int main(){ A a; std::function<void(int, int)> fr = std::bind(&A::output, &a, std::placeholders::_1,std::placeholders::_2); fr(1,2); std::function<int&(void)> fr_i = std::bind(&A::i_, &a); fr_i() = 123; cout << a.i_ << endl; return 0; }
//使用組合bind函數,找出集合中大於5小於10的元素個數 #include <iostream> #include <functional> using namespace std; auto f = std::bind(std::logical_and<bool>(),std::bind(std::greater<int>(),_1,5),std::bind(std::less_equal<int>(), _1, 10)); int main(){ set<int> se={1,2,3,4,5,6,7,8,9}; int count = std::count_if(se.begin(), se.end(), f); cout << count <<endl; return 0; }
std::bind需要注意的一些事項:(http://www.cnblogs.com/slysky/p/3822640.html)
1).std::bind預先綁定的參數需要傳具體的變量或值進去,對於預先綁定的參數,是pass-by-value的
2).對於不事先綁定的參數,需要傳std::placeholders進去,從_1開始,依次遞增。placeholder是pass-by-reference的
3).bind的返回值是可調用實體,可以直接賦給std::function對象
4).對於綁定的指針、引用類型的參數,使用者需要保證在可調用實體調用之前,這些參數是可用的
5).類的this可以通過對象或者指針來綁定