[cpp] #pragma once #include <boost/bind.hpp> #include <string> #include <iostream> #include <conio.h> #include <vector> #include <algorithm> using namespace std; using namespace boost; //bind是C++98標准庫中函數適配器bind1st、bind2nd的泛化和增強,可以適配任意的可調用對象,包括函數,函數指針,函數引用,成員函數指針和函數對象。bind遠遠的超越了STL中的函數綁定其(bind1st/bind2nd),可以綁定最多9個函數參數,而且對綁定對象的要求很低,可以在沒有result_type內部類型定義的情況下完成對函數對象的綁定。bind庫很好的增強了標准庫的功能。 //綁定普通函數(函數,函數指針) int f(int a,int b){return a+b;}//二元函數 int g(int a,int b,int c){return a+b*c;}//三元函數 void test1() { //綁定表達式沒有使用占位符 cout<<bind(f,1,2)()<<endl;//3 bind(f,1,2)返回一無參調用的函數對象,等價於f(1,2) cout<<bind(g,1,2,3)()<<endl;//7 同上 cout<<f(1,2)<<" "<<g(1,2,3)<<endl;//3 7 //使用占位符 int x=0,y=1,z=2; cout<<bind(f,_1,9)(x)<<endl;//9 ==>>f(x,9) cout<<bind(f,_1,_2)(x,y)<<endl;//1 ==>>f(x,y) cout<<bind(f,_2,_1)(x,y)<<endl;//1 ==>>f(y,x) cout<<bind(f,_1,_1)(x,y)<<endl;//0 ==>>f(x,x);y參數被忽略了 cout<<bind(g,_1,8,_2)(x,y)<<endl;//8 ==>>g(x,8,y) cout<<bind(g,_3,_2,_2)(x,y,z)<<endl;//3 ==>>g(z,y,y);x參數被忽略了 //必須在綁定表達式中提供函數要求的所有參數,無論是真實參數還是占位符均可以。但不能使用超過函數參數數量的占位符,eg:在綁定f是不能使用_3;在綁定g是不能使用_4;也不能寫bind(f,_1,_2,_2)這樣的形式,否則會導致編譯錯誤 } //綁定函數指針 用法同上,可以還有占位符,也可以不使用占位符 typedef int (*f_type)(int,int);//函數指針定義 typedef int(*g_type)(int,int,int);//函數指針定義 void test2() { f_type pf=f; g_type pg=g; int x=1,y=2,z=3; cout<<bind(pf,_1,9)(x)<<endl;//10 ==>>(*pf)(x,9) cout<<bind(pg,_3,_2,_2)(x,y,z)<<endl;//7 ==>>(*pg)(z,y,y) } //綁定成員函數 void test3() { //類的成員函數不同於普通函數,因為成員函數指針不能直接調用opreator(),它必須被綁定到一個對象或者指針上,然後才能得到this指針進而調用成員函數。因此bind需要犧牲一個占位符的位置(意味著使用成員函數是只能最多綁定8個參數),要求用戶提供一個類的實例,引用或者指針,通過對象最為第一個參數來調用成員函數。 struct demo//使用struct僅僅是為了方便,不必寫出public { int f(int a,int b){return a+b;} }; demo a,&ra=a,*p=&a;//類的實例對象,引用,指針 cout<<bind(&demo::f,a,_1,20)(10)<<endl;//30 ==>>a.f(10,20) cout<<bind(&demo::f,ra,_2,_1)(10,20)<<endl;//30 ==>>ra.f(20,10) cout<<bind(&demo::f,p,_1,_2)(10,20)<<endl;//30 ==>p->f(10,20) //注意:我們必須在成員函數前加上取地址操作符&,表明這是一個成員函數指針,否則會無法通過編譯,這是與綁定函數的一個小小的不同。 //bind能夠綁定成員函數,這是個非常有用的功能,它可以代替標准庫中令人迷惑的mem_fun和mem_fun_ref綁定器,用來配合標准算法操作容器中的對象。下面試使用bind搭配標准算法for_each用來調用容器中所有對象的print()函數: struct point { int x,y; point(int a=0,int b=0):x(a),y(b){} void print(){cout<<"("<<x<<","<<y<<") ";} }; vector<point>v(2,3); for_each(v.begin(),v.end(),bind(&point::print,_1)); //(3,0) (3,0) //bind同樣支持綁定虛擬成員函數,用法與非虛擬成員函數相同,虛函數的行為將有實際調用發生時的實例來決定。 } //綁定函數對象 void test4() { //如果函數對象有內部類型定義result_type,那麼bind可以自動推導出返回值的類型,用法與綁定普通函數一樣。但如果函數對象沒有定義result_type,則需要在綁定形式上做一點改動,用模板參數指明返回類型,eg bind<result_type>(Functor,...) //標准庫和boost庫中大部分函數對象都具有result_type定義,因此不需要特別的形式就可以直接使用bind int x=5,y=2; //cout<<bind(greater<int>(),_1,10)(x)<<endl;//0 檢查x>10 //cout<<bind(plus<int>(),_1,_2)(x,y)<<endl;//7 執行x+y //cout<<bind(modulus<int>(),_1,3)(x)<<endl;//2 執行 x%3 //上面三行在vs2010中沒問題,但在vs2008中有問題 //對於自定義函數對象,如果沒有result_type類型定義,eg struct f { int operator()(int a,int b) { return a+b;} }; cout<<bind<int>(f(),_1,_2)(x,y)<<endl;//7 //這種寫法多少會有些不方便,因此,在編寫自己的函數對象是,最好遵循規范為它們增加內部typedef result_type,這樣將使函數對象與許多其他標准庫和boost庫組件良好配合工作。 } //綁定成員變量 void test5() { struct point { int x,y; point(int a=0,int b=0):x(a),y(b){} void print(){cout<<"("<<x<<","<<y<<") ";} }; vector<point>v(2,3); vector<int>v2(2); transform(v.begin(),v.end(),v2.begin(),bind(&point::x,_1)); } //使用ref庫 void test6() {//bind采用拷貝的方式存儲綁定對象和參數,這意味著綁定表達式中的每個變量都會有一份拷貝,如果函數對象或值參數很大,拷貝代價很高,或者無法拷貝,那麼bind的使用就會受到限制。因此bind庫可以搭配ref庫使用,ref庫包裝了對象的引用,可以讓bind存儲對象引用實例,從而降低了拷貝的代價。(必須包裝引用的對象存在,其沒有銷毀) int x=10; cout<<bind(g,_1,cref(x),ref(x))(10)<<endl;//110 } void test(char t) { cout<<"press key====="<<t<<endl; switch (t) { case '1':test1();break; case '2':test2();break; case '3':test3();break; case '4':test4();break; case '5':test5();break; case '6':test6();break; case 27: case 'q':exit(0);break; default: cout<<"default "<<t<<endl;break; } } int main() { while(1) { test(getch()); } return 0; }