Boost::bind使用详解
1.Boost::bind
在STL中,我们经常需要使用bind1st,bind2st函数绑定器和fun_ptr,mem_fun等函数适配器,这些函数绑定器和函数适配器使用起来比较麻烦,需要根据是全局函数还是类的成员函数,是一个参数还是多个参数等做出不同的选择,而且有些情况使用STL提供的不能满足要求,所以如果可以我们最好使用boost提供的bind,它提供了统一的接口,提供了更多的支持,比如说它增加了shared_ptr,虚函数,类成员的绑定。
2.bind的工作原理
bind并不是一个单独的类或函数,而是非常庞大的家族,依据绑定的参数的个数和要绑定的调用对象的类型,总共有数十种不同的形式,编译器会根据具体的绑定代码制动确定要使用的正确的形式,bind的基本形式如下:
template<class R,class F> bind(F f);
template<class R,class F,class A1> bind(F f,A1 a1);
namespace
{
boost::arg<> _1;
boost::arg<> _2;
boost::arg<> _3;
….. //其他6个占位符
};
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和a1、a2的拷贝,产生了一个临时函数对象。因为func接收两个参数,而a1和a2的拷贝传递给func完成真正的函数调用。
bind的真正威力在于它的占位符,它们分别定义为_1,_2,_3,一直到 _9,位于一个匿名的名字空间。占位符可以取代bind参数的位置,在发生调用时才接受真正的参数。占位符的名字表示它在调用式中的顺序,而在绑定的表达式中没有没有顺序的要求,_1不一定必须第一个出现,也不一定只出现一次,例如:
1 bind(func,_2,_1)(a1,a2);
返回一个具有两个参数的函数对象,第一个参数将放在func的第二个位置,而第二个参数则放在第一个位置,调用时等价于:
1 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_fun<void>(&print_functionname);
some_class sc0;
std::mem_fun_ref(&some_class::print_classname)(sc0);
std::mem_fun_ref<void,some_class>(&some_class::print_classname)(sc0);
//std::mem_fun1_ref<void,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
#include <functional>
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include "boost/bind.hpp"
void main()
{
std::vector<int> ints;
ints.push_back();
ints.push_back();
ints.push_back();
ints.push_back();
int count=std::count_if(ints.begin(),
ints.end(),
boost::bind(std::logical_and<bool>(),boost::bind(std::greater<int>(),_1,),boost::bind(std::less_equal<int>(),_1,))
);
std::cout << count << '\n';
std::vector<int>::iterator int_it=std::find_if(ints.begin(),
ints.end(),
boost::bind(std::logical_and<bool>(),boost::bind(std::greater<int>(),_1,),boost::bind(std::less_equal<int>(),_1,))
);
if (int_it!=ints.end())
{ std::cout << *int_it << '\n';} }
4.3 区别传ref和传instance
// 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'; }
}; void main()
{
tracer t;
boost::bind(&tracer::print,t,_1)(std::string("I'm called on a copy of t\n"));
tracer t1;
boost::bind(&tracer::print,boost::ref(t1),_1)( std::string("I'm called directly on t\n")); }
5.bind的应用场景
5.1 绑定普通函数
bind可以绑定普通函数,包括函数、函数指针,假设我么有如下的函数定义:
int f(int a,int b){return a+b;} //二元函数
int g(int a,int b,int c) {return a+b+c;} //三元函数
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)。这两个绑定表达式没有使用占位符,而是给出了全部的具体参数,代码:
cout<<bind(f,,)()<<endl;
cout<<bind(g,,,)()<<endl;
相当于:
cout<<f(,)<<endl;
cout<<g(,,)<<endl;
使用占位符bind可以有更多的变化,这才是它真正应该做的工作,下面列出了一些占位符的用法:
bind(f,_1,)(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,,_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 =,y=,z=;
cout<<bind(pf,_1,)(x)<<endl; //(*pf(x,9))
cout<<bind(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个参数。例如,有一个类demo
struct demo
{
int f(int a,int b){return a+b;}
};
那么,下面的bind表达式都是成立的:
demo a,&ra = a; //类的实例对象和引用
demo * p = & a; //指针
cout<<bind(&demo::f,a,_1,)()<<endl;
cout<<bind(&demo::f,ra,_2,_1)(,)<<endl;
cout<<bind(&demo::f,p,_1,_2)(,)<<endl;
注意:我们必须在成员函数前面加上取地址的操作符&,表明这是一个成员函数指针,否则会无法编译通过,这是与绑定函数的一个小小的不同。bind同样支持绑定虚拟成员函数,用法与非虚函数相同,虚函数的行为将由实际调用发生时的实例来决定。
5.3 bind绑定成员变量
bind的另一个对类的操作是它可以绑定public成员变量,用法与绑定成员函数类似,只需要把成员变量名像一个成员函数一样去使用。例如:
vector<point> v();
vector<int> v2();
transform(v.begin(),v.end(),v2.begin(),bind(&point::x,_1));
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成员,例如:
typedef pair<int,string> pair_t;
pair_t p(,”string”);
cout<<bind(&pair_t::first,p)()<<endl;
cout<<bind(&pair_t::second,p)()<<endl;
5.4 绑定函数对象
bind不仅能够绑定函数和函数指针,也能够绑定任意的函数对象,包括标准库中预定义的函数对象。如果函数对象有内部类型定义result_type,那么bind可以自动推导出返回值类型,用法与普通函数一样。但如果函数对象没有定义result_type,则需要在绑定形式上做一点改动,用模板参数指明返回类型,像这样:
bind<result_type>(Functor,…);
标准库和Boost库中的大部分函数都具有result_type定义,因此不需要特别的形式就可以直接使用bind,例如:
bind(std::greater<int>(),_1,); //检查 x>10
bind(plus<int>(),_1,_2); //执行 x+y
bind(modulus<int>(),_1,), //执行 x%3
对于自定义的函数对象,如果没有result_type类型定义,例如:
struct f
{
int operator() (int a,int b) {return a +b;}
};
那么我们必须指明
bind<int> (f(),_1,_2)(,)<<endl;
这种写法所烧会有些不方便,因此,在编写自己的函数对象时,最好遵循规范为它们增加内部typedef result_type,这将使函数对象与其他的标准库和Boost库组件配合工作。
5.5 使用ref库
bind采用拷贝的方式保存绑定对象和参数,这意味着绑定表达式中的每一个变量都会有一份拷贝,如果函数对象或值参数很大、拷贝代价很高,或者无法拷贝,那么bind的使用就会受到限制。因此bind库可以搭配ref库使用,ref库包装了对象的引用,可以让bind存储对象引用的拷贝,从而降低了拷贝的代价。但这也带来了一个隐患,因为有时候bind的调用可能会延后很久,程序员必须保证bind被调用时引用是有效的。如果调用是引用的变量或者函数对象你被销毁了,那么将会发生未定义行为。ref配合bind用法的代码如下:
int x = ;
cout<<bind(g,_1,cref(x),ref(x))()<<endl;
f af;
cout<<bind<int>(ref(af),_1,_2)(,)<<endl;
下面的代码则因为引用失效,引发未定义行为:
int x = ;
BOOST_AUTO(r,ref(x));
{
int * y = new int();
r = ref(*y);
cout<<r<<endl;
cout<<bind(g,r,,)()<<endl;
delete y;
}
cout<<bind(g,r,,)()<<endl;
5.6 存储bind表达式
很多时候我们需要把写好的bind表达式存储起来,以便稍后再度使用,但bind表达式生成的对象类型声明非常复杂,通常无法写出正确的类型,因此可以使用typeof库的BOOST_AUTO宏来辅助我们,例如:
BOOST_AUTO(x,bind(greater<int>(),_1,_2));
cout<<x(,)<<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<int>(printf,…)(…),例如:
bind<int>(printf,”%d+%d=%d\n”,_1,_1,_2)(,);
bind的标准形式也不能支持使用了不同的调用方式,如:__stdcall、__fastcall、extern”C”的函数,通常bind把他们看做函数对象,需要显示的指定bind的返回值类型才能绑定。
原文链接:
http://www.cppblog.com/mzty/archive/2007/09/05/31622.html
http://www.cnblogs.com/yu-chao/p/3979124.html
Boost::bind使用详解的更多相关文章
- c/c++ 标准库 bind 函数 详解
标准库 bind 函数 详解 bind函数:接收一个函数名作为参数,生成一个新的函数. auto newCallable = bind(callbale, arg_list); arg_list中的参 ...
- [转] boost::function用法详解
http://blog.csdn.net/benny5609/article/details/2324474 要开始使用 Boost.Function, 就要包含头文件 "boost/fun ...
- boost::function用法详解
要开始使用 Boost.Function, 就要包含头文件 "boost/function.hpp", 或者某个带数字的版本,从 "boost/function/func ...
- boost::fucntion 用法详解
转载自:http://blog.csdn.net/benny5609/article/details/2324474 要开始使用 Boost.Function, 就要包含头文件 "boost ...
- call(),apply()和bind()的详解使用:
obj.call(thisObj, arg1, arg2, ...); obj.apply(thisObj, [arg1, arg2, ...]); 两者作用一致,都是把obj(即this)绑定到th ...
- boost库asio详解1——strand与io_service区别
namespace { // strand提供串行执行, 能够保证线程安全, 同时被post或dispatch的方法, 不会被并发的执行. // io_service不能保证线程安全 boost::a ...
- 【Boost】boost库asio详解3——io_service作为work pool
无论如何使用,都能感觉到使用boost.asio实现服务器,不仅是一件非常轻松的事,而且代码很漂亮,逻辑也相当清晰,这点上很不同于ACE.使用io_service作为处理工作的work pool,可以 ...
- boost库asio详解8——几个TCP的简单例子
摘于boost官网的几个例子, 做了点小修改, 笔记之. 同步客户端 void test_asio_synclient() { typedef boost::asio::io_service IoSe ...
- (十一)socket、connect、bind函数详解
一.socket函数 1.头文件: #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> 2.函数原型: ...
随机推荐
- SVN简单使用
如果是window操作系统,默认安装.右键菜单就会有显示SVN 如果已经配置好SVN,直接确定既可以检出. 如果没有配置,那么会显示下面的验证: 输入用户名和密码即可
- lambda函数的特性
lambda表达式可以理解为一种抽象的函数实现方法,这种方式只有最基本的三个步骤:给与参数,表达式实现,返回结果.这种方式非常干净,减少了内存的使用,整个程序少了函数的污染,代码格式也会更为简练.但在 ...
- mysql 插入更新判断 ON DUPLICATE KEY UPDATE 和 REPLACE INTO
平时我们在设计数据库表的时候总会设计 unique 或者 给表加上 primary key 的限制条件.此时 插入数据的时候 ,经常会有这样的情况:我们想向数据库插入一条记录: 若数据表中存在以相同主 ...
- [转][Echarts]俄罗斯方块
app.title = '俄罗斯方块'; var refreshT,fallBlockT; var fallTimout; var speed = 1000, downSpeed = 30, nomr ...
- 2、初探 ZooKeeper 技术内幕
分布式一致性 “分布式” 是大型系统实现高性能.高可用所常用的架构手段,本章节将概述 “分布式一致性”的基本内容,以作为 ZAB 算法阐述的基础. 分布式一致性的基本概念 数据库系统的基础理论中,“事 ...
- Android 获取控件滑动速度,速度跟踪器VelocityTracker;
VelocityTracker 速度跟踪器 在写关于Android滑动的控件,如果用户手指在屏幕上(当前位置 - 起始位置 > 某个数值)就做一个界面切换,但是总感觉太生硬,只有满足上面的条件才 ...
- attr 和 prop 的区别
在高版本的jquery引入prop方法后,什么时候该用prop?什么时候用attr?它们两个之间有什么区别?这些问题就出现了. 关于它们两个的区别,网上的答案很多.这里谈谈我的心得,我的心得很简单: ...
- 浏览器局部打印实现,iframe打印
const handleOk = () =>{ let ele = document.getElementById('printInfor'); let iframe=window.frames ...
- 关于Haclon使用GPU加速的代码实例
关于Haclon使用GPU加速的代码实例 read_image(Image, 'T20170902014819_58_2_1.bmp') *没有加加速并行处理 count_seconds(T1) to ...
- python大法好——模块(内置模块未完)
模块 模块是非常简单的Python文件,单个Python文件就是一个模块,两个文件就是两个模块. Python模块有什么作用? 1.模块内有许多函数方法,利用这些方法可以更简单的完成许多工作.2.模块 ...