1.boost::bind在STL中我们经常需要使用bind1stbind2st函数绑定器和fun_ptr,mem_fun等函数适配器这些函数绑定器和函数适配器使用起来比较麻烦需要根据是全局函数还是类的成员函数是一个参数还是多个参数等做出不同的选择而且有些情况使用STL提供的不能满足要求所以如果可以我们最好使用boost提供的bind它提供了统一的接口提供了更多的支持比如说它增加了shared_ptr虚函数类成员的绑定。boost :: bind是标准函数std :: bind1st和std :: bind2nd的泛化。 它支持任意函数对象函数函数指针和成员函数指针并且能够将任何参数绑定到特定值或将输入参数路由到任意位置。 bind不对函数对象提出任何要求; 特别是它不需要result_typefirst_argument_type和second_argument_type标准typedefs.2.bind的工作原理bind并不是一个单独的类或函数而是非常庞大的家族依据绑定的参数的个数和要绑定的调用对象的类型总共有数十种不同的形式编译器会根据具体的绑定代码制动确定要使用的正确的形式bind的基本形式如下通过打开文件bind.hpp可以看到ind并不是一个单独的类或函数而是非常庞大的家族依据绑定的参数的个数和要绑定的调用对象的类型总共有数十种不同的形式编译器会根据具体的绑定代码制动确定要使用的正确的形式bind的基本形式如下func(a1,a2);那么他将等价于一个具有无参operator()的bind函数对象调用bind(func,a1,a2)();这是bind最简单的形式bind表达式存储了func和a1、a2的拷贝产生了一个临时函数对象。因为func接收两个参数而a1和a2的拷贝传递给func完成真正的函数调用。bind的真正威力在于它的占位符它们分别定义为_1,_2,_3,一直到 _9,位于一个匿名的名字空间。占位符可以取代bind参数的位置在发生调用时才接受真正的参数。占位符的名字表示它在调用式中的顺序而在绑定的表达式中没有没有顺序的要求_1不一定必须第一个出现也不一定只出现一次例如bind(func,_2,_1)(a1,a2);返回一个具有两个参数的函数对象第一个参数将放在func的第二个位置而第二个参数则放在第一个位置调用时等价于func(a2,a1);3.常用的函数对象工具(1)bind1st,bind2st函数绑定器把二元函数对象变为一元函数对象。(2)mem_fun,把成员函数变为函数对象。(3)fun_ptr把一般的全局函数变为函数对象。(4)boost::bind(),包含了以上所有的功能。4.bind与其他函数对象工具区别4.1 区别与mem_fun和fun_ptr#include functional #include iostream #include string #include boost/bind.hpp class some_class { public: void print_string(const std::string s) const { std::cout s \n; } void print_classname() { std::cout some_class std::endl; } }; void print_string(const std::string s) { std::cout s \n; } void print_functionname() { std::cout Print_functionname std::endl; } int main() { std::ptr_fun(print_string)(hello1); //std::ptr_funvoid(print_functionname); some_class sc0; std::mem_fun_ref(some_class::print_classname)(sc0); std::mem_fun_refvoid,some_class(some_class::print_classname)(sc0); //std::mem_fun1_refvoid,some_class,const std::stirng(some_class::print_string)(sc0,hello2); (boost::bind(print_string,_1))(Hello func!); boost::bind(print_functionname)(); some_class sc; (boost::bind(some_class::print_classname,_1)(sc)); (boost::bind(some_class::print_string,_1,_2))(sc,Hello member!); }运行结果如下:4.2 区别与bind1st和bind2st// // Created by felaim on 7/12/18. // #include functional #include iostream #include string #include vector #include algorithm #include boost/bind.hpp int main() { std::vectorint ints; ints.push_back(7); ints.push_back(4); ints.push_back(12); ints.push_back(10); int count std::count_if(ints.begin(), ints.end(), boost::bind(std::logical_andbool(), boost::bind(std::greaterint(), _1, 5), boost::bind(std::less_equalint(), _1, 10)) ); std::cout count \n; std::vectorint::iterator int_it std::find_if(ints.begin(), ints.end(), boost::bind(std::logical_andbool(), boost::bind(std::greaterint(), _1, 5), boost::bind(std::less_equalint(), _1, 10)) ); if (int_it ! ints.end()) { std::cout *int_it \n; } int count_1st std::count_if(ints.begin(), ints.end(), std::bind1st(std::equal_toint(), 10)); std::cout There are count_1st elements that are equal to 10 std::endl; int count_2nd std::count_if(ints.begin(), ints.end(), std::bind2nd(std::less_equalint(), 10)); std::cout There are count_2nd elements that are less or equal 10 std::endl; return 0; } }4.3 区别传ref和传instance// // Created by felaim on 7/12/18. // // bind instance or reference #include functional #include iostream #include string #include vector #include algorithm #include boost/bind.hpp class tracer { public: tracer() { std::cout tracer::tracer()\n; } tracer(const tracer other) { std::cout tracer::tracer(const tracer other)\n; } tracer operator(const tracer other) { std::cout tracer tracer::operator(const tracer other)\n; return *this; } ~tracer() { std::cout tracer::~tracer()\n; } void print(const std::string s) const { std::cout s \n; } }; int main() { tracer t; boost::bind(tracer::print,t,_1)(std::string(Im called on a copy of t\n)); tracer t1; boost::bind(tracer::print,boost::ref(t1),_1)( std::string(Im called directly on t\n)); return 0; }5.bind的应用场景5.1 绑定普通函数bind可以绑定普通函数包括函数、函数指针假设我么有如下的函数定义int f(int a,int b){return ab;} //二元函数 int g(int a,int b,int c) {return abc;} //三元函数 typedef int (*f_type)(int,int); //函数指针定义 typedef int (*g_type)(int,int,int); //函数指针定义那么bind(f,1,2) 将返回一个无参调用函数对象等价于f(1,2),bind(q,1,2,3)同样返回一个无参调用的函数对象等价于 g(1,2,3)。这两个绑定表达式没有使用占位符而是给出了全部的具体参数代码coutbind(f,1,2)()endl; coutbind(g,1,2,3)()endl;等价于coutf(1,2)endl; coutg(1,2,3)endl;使用占位符bind可以有更多的变化这才是它真正应该做的工作下面列出了一些占位符的用法bind(f,_1,9)(x); //f(x,9),相当于bind2nd(f,9) bind(f,_1,_2)(x,y); //f(x,y) bind(f,_2,_1)(x,y); //f(y,x) bind(f,_1,_1)(x,y); //f(x,x),y参数被忽略 bind(g,_1,8,_2)(x,y) //g(x,8,y) 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也可以绑定函数指针用法相同例如f_type pf f; g_type pg g; int x 1,y2,z3; coutbind(pf,_1,9)(x)endl; //(*pf(x,9)) coutbind(pg,_3,_2,_2)(x,y,z)endl; //(*pg)(z,y,y)5.2 bind绑定成员函数类的成员函数不同于普通的函数因为成员函数指针不能直接调用operator(),它必须被绑定到一个对象或指针然后才能得到this指针进而调用成员函数。因此bind需要 “牺牲”一个占位符要求提供一个类的实例、引用或者指针通过对象作为第一个参数来调用成员函数即bind(X::func,x,_1,_2,…)这意味着使用成员函数时只能最多绑定8个参数。例如有一个类demostruct demo { int f(int a,int b){return ab;} };那么下面的bind表达式都是成立的// // Created by felaim on 7/12/18. // #include iostream #include string #include boost/bind.hpp using namespace std; struct demo { int f(int a, int b){return ab;} }; int main(int argv, char **argc) { demo a, ra a; //类的实例对象和引用 demo * p a; // 指针 cout boost::bind(demo::f, a, _1, 20)(10) endl; cout boost::bind(demo::f, ra, _2, _1)(10, 20) endl; cout boost::bind(demo::f, p, _1, _2)(10, 20) endl; }注意我们必须在成员函数前面加上取地址的操作符表明这是一个成员函数指针否则会无法编译通过这是与绑定函数的一个小小的不同。bind同样支持绑定虚拟成员函数用法与非虚函数相同虚函数的行为将由实际调用发生时的实例来决定。5.3 bind绑定成员变量bind的另一个对类的操作是它可以绑定public成员变量用法与绑定成员函数类似只需要把成员变量名像一个成员函数一样去使用。例如vectorpoint v(10); vectorint v2(10); transform(v.begin(),v.end(),v2.begin(),bind(point::x,_1)); BOOST_FOREACH(int x,v2) coutx“,”;代码中的bind(point::x,_1)取出point对象的成员变量xtransform算法调用bind表达式操作容器v,逐个把成员变量填入到v2中。看到这里感到有点困惑有点难以理解bind返回的是一个函数对象该对象对“”进行了重载在transform调用该重载函数应该是将v中的每一个成员变量作为参数传进去从而取出每一个元素的x变量。// // Created by felaim on 7/13/18. // // bind classs member #include functional #include iostream #include string #include vector #include algorithm #include boost/bind.hpp class personal_info { std::string name_; std::string surname_; unsigned int age_; public: personal_info(const std::string n,const std::string s,unsigned int age):name_(n),surname_(s),age_(age) {} std::string name() const {return name_;} std::string surname() const {return surname_;} unsigned int age() const {return age_;} }; int main() { std::vectorpersonal_info vec; vec.push_back(personal_info(Little,John,30)); vec.push_back(personal_info(Friar, Tuck,50)); vec.push_back(personal_info(Robin, Hood,40)); std::sort(vec.begin(), vec.end(), boost::bind(std::lessunsigned int(),boost::bind(personal_info::age,_1),boost::bind(personal_info::age,_2)) ); std::cout vec[0].name() vec[0].surname() vec[0].age() std::endl; std::cout vec[1].name() vec[1].surname() vec[1].age() std::endl; std::cout vec[2].name() vec[2].surname() vec[2].age() std::endl std::endl; std::sort(vec.begin(), vec.end(), boost::bind(std::lessstd::string(),boost::bind(personal_info::surname,_1),boost::bind(personal_info::surname,_2)) ); std::cout vec[0].name() vec[0].surname() vec[0].age() std::endl; std::cout vec[1].name() vec[1].surname() vec[1].age() std::endl; std::cout vec[2].name() vec[2].surname() vec[2].age() std::endl; return 0; }使用bind可以实现SGISTL/STLport中的非标准函数适配器select1st和select2nd的功能直接选择出pair对象first和second成员例如// // Created by felaim on 7/13/18. // #include iostream #include string #include boost/bind.hpp #include vector using namespace std; typedef pairint,string pair_t; int main(int argc, char **argv) { pair_t p(123,string); coutbind(pair_t::first,p)()endl; coutbind(pair_t::second,p)()endl; return 0; }5.4 绑定函数对象bind不仅能够绑定函数和函数指针也能够绑定任意的函数对象包括标准库中预定义的函数对象。如果函数对象有内部类型定义result_type那么bind可以自动推导出返回值类型用法与普通函数一样。但如果函数对象没有定义result_type则需要在绑定形式上做一点改动用模板参数指明返回类型像这样bindresult_type(Functor,…);标准库和Boost库中的大部分函数都具有result_type定义因此不需要特别的形式就可以直接使用bind,例如bind(std::greaterint(),_1,10); //检查 x10 bind(plusint(),_1,_2); //执行 xy bind(modulusint(),_1,3), //执行 x%3对于自定义的函数对象如果没有result_type类型定义例如struct f { int operator() (int a,int b) {return a b;} };下面仅仅进行简单的测试,对上述实例更直观的了解:// // Created by felaim on 7/13/18. // #include iostream #include string #include boost/bind.hpp #include functional using namespace std; struct f { int operator() (int a,int b) {return a b;} }; int main(int argc, char **argv){ bind(std::greaterint(),_1,10); cout bind(std::greaterint(),_1,10)(11) endl; //检查 x10 cout bind(plusint(),_1,_2)(1,4) endl; //执行 xy cout bind(modulusint(),_1,3)(7) endl;//执行 x%3 cout bindint (f(),_1,_2)(10,20)endl; return 0; }这种写法会有些不方便因此在编写自己的函数对象时最好遵循规范为它们增加内部typedef result_type,这将使函数对象与其他的标准库和Boost库组件配合工作。5.5 使用ref库bind采用拷贝的方式保存绑定对象和参数这意味着绑定表达式中的每一个变量都会有一份拷贝如果函数对象或值参数很大、拷贝代价很高或者无法拷贝那么bind的使用就会受到限制。因此bind库可以搭配ref库使用ref库包装了对象的引用可以让bind存储对象引用的拷贝从而降低了拷贝的代价。但这也带来了一个隐患因为有时候bind的调用可能会延后很久程序员必须保证bind被调用时引用是有效的。如果调用是引用的变量或者函数对象你被销毁了那么将会发生未定义行为。ref配合bind用法的代码如下int x 10; coutbind(g,_1,cref(x),ref(x))(10)endl; f af; coutbindint(ref(af),_1,_2)(10,20)endl;下面的代码则因为引用失效引发未定义行为int x 10; BOOST_AUTO(r,ref(x)); { int * y new int(20); r ref(*y); coutrendl; coutbind(g,r,1,1)()endl; delete y; } coutbind(g,r,1,1)()endl;5.6 存储bind表达式很多时候我们需要把写好的bind表达式存储起来以便稍后再度使用但bind表达式生成的对象类型声明非常复杂通常无法写出正确的类型因此可以使用typeof库的BOOST_AUTO宏来辅助我们例如BOOST_AUTO(x,bind(greaterint(),_1,_2)); coutx(10,20)endl;bind可以嵌套一个bind表达式生成的函数对象可以被另一个bind再绑定从而实现类似f(g(x))的形式如果我们有f(x)和g(x)两个函数那么f(g(x))的bind表达式就是bind(f,bind(g,_1))(x)使用bind的嵌套用法必须小心它不太容易一次写对也不太容易理解超过两个以上的bind表达式通常只能被编译器读懂必须配合良好的注释才能够使用bind嵌套用法。5.7 绑定非标准函数bind可以适配任何一种C中的函数但标准形式bind(f,…)不是适用所用的情况有些非标准函数无法制动推导出返回值类型典型的就是C中的可变参数函数printf()。必须用bind(printf,…)(…)例如bindint(printf,”%d%d%d\n”,_1,_1,_2)(6,7);bind的标准形式也不能支持使用了不同的调用方式如__stdcall、__fastcall、extern”C”的函数通常bind把他们看做函数对象需要显示的指定bind的返回值类型才能绑定。参考网址:https://www.boost.org/doc/libs/1_60_0/libs/bind/doc/html/bind.htmlhttps://www.cnblogs.com/blueoverflow/p/4740093.htmlhttp://www.cppblog.com/mzty/archive/2007/09/05/31622.htmlhttp://www.cnblogs.com/yu-chao/p/3979124.html