在STL中,咱们常常须要使用bind1st,bind2st函数绑定器和fun_ptr,mem_fun等函数适配器,这些函数绑定器和函数适配器使用起来比较麻烦,须要根据是全局函数仍是类的成员函数,是一个参数仍是多个参数等作出不一样的选择,并且有些状况使用STL提供的不能知足要求,因此若是能够咱们最好使用boost提供的bind,它提供了统一的接口,提供了更多的支持,好比说它增长了shared_ptr,虚函数,类成员的绑定。html
bind并非一个单独的类或函数,而是很是庞大的家族,依据绑定的参数的个数和要绑定的调用对象的类型,总共有数十种不一样的形式,编译器会根据具体的绑定代码制动肯定要使用的正确的形式,bind的基本形式以下:ios
1 template<class R,class F> bind(F f); 2 template<class R,class F,class A1> bind(F f,A1 a1); 3 namespace 4 { 5 boost::arg<1> _1; 6 boost::arg<2> _2; 7 boost::arg<3> _3; 8 ….. //其余6个占位符 9 };
bind接收的第一个参数必须是一个可调用的对象f,包括函数、函数指针、函数对象、和成员函数指针,以后bind最多接受9个参数,参数数量必须与f的参数数量相等,这些参数被传递给f做为入参。 绑定完成后,bind会返回一个函数对象,它内部保存了f的拷贝,具备operator(),返回值类型被自动推导为f的返回类型。在发生调用时这个函数对象将把以前存储的参数转发给f完成调用。例如,有一个函数func,它的形式是:程序员
1 func(a1,a2);
那么,他将等价于一个具备无参operator()的bind函数对象调用:算法
1 bind(func,a1,a2)();
这是bind最简单的形式,bind表达式存储了func和a一、a2的拷贝,产生了一个临时函数对象。由于func接收两个参数,而a1和a2的拷贝传递给func完成真正的函数调用。less
bind的真正威力在于它的占位符,它们分别定义为_1,_2,_3,一直到 _9,位于一个匿名的名字空间。占位符能够取代bind参数的位置,在发生调用时才接受真正的参数。占位符的名字表示它在调用式中的顺序,而在绑定的表达式中没有没有顺序的要求,_1不必定必须第一个出现,也不必定只出现一次,例如:ide
1 bind(func,_2,_1)(a1,a2);
返回一个具备两个参数的函数对象,第一个参数将放在func的第二个位置,而第二个参数则放在第一个位置,调用时等价于:函数
1 func(a2,a1);
(1)bind1st,bind2st函数绑定器,把二元函数对象变为一元函数对象。
(2)mem_fun,把成员函数变为函数对象。
(3)fun_ptr,把通常的全局函数变为函数对象。
(4)boost::bind(),包含了以上全部的功能。工具
1 #include <functional> 2 #include <iostream> 3 #include <string> 4 #include "boost/bind.hpp" 5 class some_class 6 { 7 public: 8 void print_string(const std::string& s) const 9 { 10 std::cout << s << '\n'; 11 } 12 void print_classname() 13 { 14 std::cout << "some_class" << std::endl; 15 } 16 }; 17 void print_string(const std::string s) 18 { std::cout << s << '\n'; 19 } 20 void print_functionname() 21 { 22 std::cout << "Print_functionname" <<std::endl; 23 } 24 int main() 25 { 26 std::ptr_fun(&print_string)("hello1"); 27 //std::ptr_fun<void>(&print_functionname); 28 some_class sc0; 29 std::mem_fun_ref(&some_class::print_classname)(sc0); 30 std::mem_fun_ref<void,some_class>(&some_class::print_classname)(sc0); 31 //std::mem_fun1_ref<void,some_class,const std::stirng>(&some_class::print_string)(sc0,"hello2"); 32 33 (boost::bind(&print_string,_1))("Hello func!"); 34 boost::bind(&print_functionname); 35 some_class sc; 36 (boost::bind(&some_class::print_classname,_1)(sc)); 37 (boost::bind(&some_class::print_string,_1,_2))(sc,"Hello member!"); 38 }
1 #include <functional> 2 #include <iostream> 3 #include <string> 4 #include <vector> 5 #include <algorithm> 6 #include "boost/bind.hpp" 7 void main() 8 { 9 std::vector<int> ints; 10 ints.push_back(7); 11 ints.push_back(4); 12 ints.push_back(12); 13 ints.push_back(10); 14 int count=std::count_if(ints.begin(), 15 ints.end(), 16 boost::bind(std::logical_and<bool>(),boost::bind(std::greater<int>(),_1,5),boost::bind(std::less_equal<int>(),_1,10)) 17 ); 18 std::cout << count << '\n'; 19 std::vector<int>::iterator int_it=std::find_if(ints.begin(), 20 ints.end(), 21 boost::bind(std::logical_and<bool>(),boost::bind(std::greater<int>(),_1,5),boost::bind(std::less_equal<int>(),_1,10)) 22 ); 23 if (int_it!=ints.end()) 24 { std::cout << *int_it << '\n';} 25 26 }
1 // bind instance or reference 2 #include <functional> 3 #include <iostream> 4 #include <string> 5 #include <vector> 6 #include <algorithm> 7 #include "boost/bind.hpp" 8 class tracer 9 { 10 public: 11 tracer() { std::cout << "tracer::tracer()\n"; } 12 tracer(const tracer& other) { std::cout << "tracer::tracer(const tracer& other)\n"; } 13 tracer& operator=(const tracer& other) 14 { std::cout << "tracer& tracer::operator=(const tracer& other)\n"; return *this; } 15 ~tracer() { std::cout << "tracer::~tracer()\n"; 16 } 17 void print(const std::string& s) const 18 { std::cout << s << '\n'; } 19 }; 20 21 void main() 22 { 23 tracer t; 24 boost::bind(&tracer::print,t,_1)(std::string("I'm called on a copy of t\n")); 25 tracer t1; 26 boost::bind(&tracer::print,boost::ref(t1),_1)( std::string("I'm called directly on t\n")); 27 28 }
bind能够绑定普通函数,包括函数、函数指针,假设我么有以下的函数定义:this
1 int f(int a,int b){return a+b;} //二元函数 2 int g(int a,int b,int c) {return a+b+c;} //三元函数 3 typedef int (*f_type)(int,int); //函数指针定义 4 typedef int (*g_type)(int,int,int); //函数指针定义
那么,bind(f,1,2) 将返回一个无参调用函数对象,等价于f(1,2),bind(q,1,2,3)一样返回一个无参调用的函数对象,等价于 g(1,2,3)。这两个绑定表达式没有使用占位符,而是给出了所有的具体参数,代码:spa
1 cout<<bind(f,1,2)()<<endl; 2 cout<<bind(g,1,2,3)()<<endl;
至关于:
1 cout<<f(1,2)<<endl; 2 cout<<g(1,2,3)<<endl;
使用占位符bind能够有更多的变化,这才是它真正应该作的工做,下面列出了一些占位符的用法:
1 bind(f,_1,9)(x); //f(x,9),至关于bind2nd(f,9) 2 bind(f,_1,_2)(x,y); //f(x,y) 3 bind(f,_2,_1)(x,y); //f(y,x) 4 bind(f,_1,_1)(x,y); //f(x,x),y参数被忽略 5 bind(g,_1,8,_2)(x,y) //g(x,8,y) 6 bind(g,_3,_2_2)(x,y,z) //g(z,y,y),x参数被忽略
注意:必须在绑定表达式中提供函数要求的全部参数,不管是真实参数仍是占位符都可以。占位符能够出现也能够不出现,出现的顺序和数量没有限定,但不能使用超过函数参数数量的占位符,好比在绑定f是不能用_3,在绑定g时不能使用_4,也不能写bind(f,_1,_2,_2),这样的形式会致使编译错误。bind彻底能够代替标准库中的bind1st和bind2nd,使用bind(f,N,_1)和bind(f,_1,N)。要注意的是它们均使用了一个占位符,bind1st把第一个参数用固定值代替,bind2nd把第二个参数用固定值代替。bind也能够绑定函数指针,用法相同,例如:
1 f_type pf = f; 2 g_type pg = g; 3 int x =1,y=2,z=3; 4 cout<<bind(pf,_1,9)(x)<<endl; //(*pf(x,9)) 5 cout<<bind(pg,_3,_2,_2)(x,y,z)<<endl; //(*pg)(z,y,y)
类的成员函数不一样于普通的函数,由于成员函数指针不能直接调用operator(),它必须被绑定到一个对象或指针,而后才能获得this指针进而调用成员函数。所以bind须要 “牺牲”一个占位符,要求提供一个类的实例、引用或者指针,经过对象做为第一个参数来调用成员函数,即:
1 bind(&X::func,x,_1,_2,…)
这意味着使用成员函数时只能最多绑定8个参数。例如,有一个类demo
1 struct demo 2 { 3 int f(int a,int b){return a+b;} 4 };
那么,下面的bind表达式都是成立的:
1 demo a,&ra = a; //类的实例对象和引用 2 demo * p = & a; //指针 3 cout<<bind(&demo::f,a,_1,20)(10)<<endl; 4 cout<<bind(&demo::f,ra,_2,_1)(10,20)<<endl; 5 cout<<bind(&demo::f,p,_1,_2)(10,20)<<endl;
注意:咱们必须在成员函数前面加上取地址的操做符&,代表这是一个成员函数指针,不然会没法编译经过,这是与绑定函数的一个小小的不一样。bind一样支持绑定虚拟成员函数,用法与非虚函数相同,虚函数的行为将由实际调用发生时的实例来决定。
bind的另外一个对类的操做是它能够绑定public成员变量,用法与绑定成员函数相似,只须要把成员变量名像一个成员函数同样去使用。例如:
1 vector<point> v(10); 2 vector<int> v2(10); 3 transform(v.begin(),v.end(),v2.begin(),bind(&point::x,_1)); 4 BOOST_FOREACH(int x,v2) cout<<x<<“,”;
代码中的bind(&point::x,_1)取出point对象的成员变量x,transform算法调用bind表达式操做容器v,逐个把成员变量填入到v2中。看到这里感到有点困惑,有点难以理解:bind返回的是一个函数对象,该对象对“()”进行了重载,在transform调用该重载函数应该是将v中的每个成员变量做为参数传进去,从而取出每个元素的x变量。
使用bind能够实现SGISTL/STLport中的非标准函数适配器select1st和select2nd的功能,直接选择出pair对象first和second成员,例如:
1 typedef pair<int,string> pair_t; 2 pair_t p(123,”string”); 3 cout<<bind(&pair_t::first,p)()<<endl; 4 cout<<bind(&pair_t::second,p)()<<endl;
bind不只可以绑定函数和函数指针,也可以绑定任意的函数对象,包括标准库中预约义的函数对象。若是函数对象有内部类型定义result_type,那么bind能够自动推导出返回值类型,用法与普通函数同样。但若是函数对象没有定义result_type,则须要在绑定形式上作一点改动,用模板参数指明返回类型,像这样:
1 bind<result_type>(Functor,…);
标准库和Boost库中的大部分函数都具备result_type定义,所以不须要特别的形式就能够直接使用bind,例如:
1 bind(std::greater<int>(),_1,10); //检查 x>10 2 bind(plus<int>(),_1,_2); //执行 x+y 3 bind(modulus<int>(),_1,3), //执行 x%3
对于自定义的函数对象,若是没有result_type类型定义,例如:
1 struct f 2 { 3 int operator() (int a,int b) {return a +b;} 4 };
那么咱们必须指明
1 bind<int> (f(),_1,_2)(10,20)<<endl;
这种写法所烧会有些不方便,所以,在编写本身的函数对象时,最好遵循规范为它们增长内部typedef result_type,这将使函数对象与其余的标准库和Boost库组件配合工做。
bind采用拷贝的方式保存绑定对象和参数,这意味着绑定表达式中的每个变量都会有一份拷贝,若是函数对象或值参数很大、拷贝代价很高,或者没法拷贝,那么bind的使用就会受到限制。所以bind库能够搭配ref库使用,ref库包装了对象的引用,可让bind存储对象引用的拷贝,从而下降了拷贝的代价。但这也带来了一个隐患,由于有时候bind的调用可能会延后好久,程序员必须保证bind被调用时引用是有效的。若是调用是引用的变量或者函数对象你被销毁了,那么将会发生未定义行为。ref配合bind用法的代码以下:
1 int x = 10; 2 cout<<bind(g,_1,cref(x),ref(x))(10)<<endl; 3 f af; 4 cout<<bind<int>(ref(af),_1,_2)(10,20)<<endl;
下面的代码则由于引用失效,引起未定义行为:
1 int x = 10; 2 BOOST_AUTO(r,ref(x)); 3 { 4 int * y = new int(20); 5 r = ref(*y); 6 cout<<r<<endl; 7 cout<<bind(g,r,1,1)()<<endl; 8 delete y; 9 } 10 cout<<bind(g,r,1,1)()<<endl;
不少时候咱们须要把写好的bind表达式存储起来,以便稍后再度使用,但bind表达式生成的对象类型声明很是复杂,一般没法写出正确的类型,所以可使用typeof库的BOOST_AUTO宏来辅助咱们,例如:
1 BOOST_AUTO(x,bind(greater<int>(),_1,_2)); 2 cout<<x(10,20)<<endl;
bind能够嵌套,一个bind表达式生成的函数对象能够被另外一个bind再绑定,从而实现相似f(g(x))的形式,若是咱们有f(x)和g(x)两个函数,那么f(g(x))的bind表达式就是:
1 bind(f,bind(g,_1))(x)
使用bind的嵌套用法必须当心,它不太容易一次写对,也不太容易理解,超过两个以上的bind表达式一般只能被编译器读懂,必须配合良好的注释才可以使用bind嵌套用法。
bind能够适配任何一种C++中的函数,但标准形式bind(f,…)不是适用所用的状况,有些非标准函数没法制动推导出返回值类型,典型的就是C中的可变参数函数printf()。必须用bind<int>(printf,…)(…),例如:
1 bind<int>(printf,”%d+%d=%d\n”,_1,_1,_2)(6,7);
bind的标准形式也不能支持使用了不一样的调用方式,如:__stdcall、__fastcall、extern”C”的函数,一般bind把他们看作函数对象,须要显示的指定bind的返回值类型才能绑定。
原文连接: