C++ 11 std::function和std::bind使用详解
cocosnew出新的项目之后,仔细阅读代码,才发现了一句3.0区别于2.0的代码:
autocloseItem=MenuItemImage::create( "CloseNormal.png", "CloseSelected.png", CC_CALLBACK_1(HelloWorld::menuCloseCallback,this));
2.0内的代码用的不是CC_CALLBACK_1而是menu_selector.
CC_CALLBACK系列是3.0基于c++11的特性新增的。CC_CALLBACK系列的定义如下:
//newcallbacksbasedonC++11 #defineCC_CALLBACK_0(__selector__,__target__,...)std::bind(&__selector__,__target__,##__VA_ARGS__) #defineCC_CALLBACK_1(__selector__,__target__,...)std::bind(&__selector__,__target__,std::placeholders::_1,##__VA_ARGS__) #defineCC_CALLBACK_2(__selector__,__target__,...)std::bind(&__selector__,__target__,std::placeholders::_1,std::placeholders::_2,##__VA_ARGS__) #defineCC_CALLBACK_3(__selector__,__target__,...)std::bind(&__selector__,__target__,std::placeholders::_1,std::placeholders::_2,std::placeholders::_3,##__VA_ARGS__)
可以看出,CC_CALL_BACK系统后的数字,表示函数指针的参数个数。明白了这一点,选择CC_CALLBACK时,就不会出错鸟。
而看示例代码时,还会发现一个有意思的使用方法:
listener->onTouchesBegan=CC_CALLBACK_2(Layer::onTouchesBegan,this);
此时不禁要问onTouchesBegan又是啥,为啥不能直接函数指针赋值呢?
看定义就能明白了
std::function&,Event*)>onTouchesBegan;
因为CC_CALLBACK系列是std::bind,而onTouchesBegan是std::function来定义的。那么std::bind和std::function又有什么区别呢?
有博文说:
function模板类和bind模板函数,使用它们可以实现类似函数指针的功能,但却比函数指针更加灵活,特别是函数指向类的非静态成员函数时。
std::function可以绑定到全局函数/类静态成员函数(类静态成员函数与全局函数没有区别),如果要绑定到类的非静态成员函数,则需要使用std::bind。
标准库函数bind()和function()定义于头文件
std::bind绑定器
- 将函数、成员函数和闭包转成function函数对象
- 将多元(n>1)函数转成一元函数或者(n-1)元函数。
bind()接受一个函数(或者函数对象,或者任何你可以通过"(...)"符号调用的事物),生成一个其有某一个或多个函数参数被“绑定”或重新组织的函数对象。(译注:顾名思义,bind()函数的意义就像它的函数名一样,是用来绑定函数调用的某些参数的。)例如:
intf(int,char,double); autoff=bind(f,_1,'c',1.2);//绑定f()函数调用的第二个和第三个参数,返回一个新的函数对象为ff,它只带有一个int类型的参数 intx=ff(7);//f(7,'c',1.2);
参数的绑定通常称为"Currying"(译注:Currying---“烹制咖喱烧菜”,此处意指对函数或函数对象进行加工修饰操作),"_1"是一个占位符对象,用于表示当函数f通过函数ff进行调用时,函数ff的第一个参数在函数f的参数列表中的位置。第一个参数称为"_1",第二个参数为"_2",依此类推。例如:
intf(int,char,double); autofrev=bind(f,_3,_2,_1);//翻转参数顺序 intx=frev(1.2,'c',7);//f(7,'c',1.2);
此处,auto关键字节约了我们去推断bind返回的结果类型的工作。
我们无法使用bind()绑定一个重载函数的参数,我们必须显式地指出需要绑定的重载函数的版本:
intg(int); doubleg(double); autog1=bind(g,_1);//错误:调用哪一个g()? autog2=bind((double(*)(double))g,_1);//正确,但是相当丑陋
voidH(inta); //绑定全局函数 autof11=std::bind(H,std::placeholders::_1); auto的类型实际上是std::function//绑定带参数的成员函数 std::function f=std::bind(&ReadHandler::ConnectPreProcess,this,std::placeholders::_1,std::placeholders::_1); //三元函数转换成一元函数 intf(int,char,double); //绑定f()函数调用的第二个和第三个参数, //返回一个新的函数对象为ff,它只带有一个int类型的参数 autoff=bind(f,_1,‘c',1.2); intx=ff(7);
自己写代码示例如下:
intFunc(intx,inty); autobf1=std::bind(Func,10,std::placeholders::_1); bf1(20);///bf3=std::bind(&HelloWorld::AddFunc,this,std::placeholders::_1,100); autoresult2=bf3(10);/// 上面的例子中,bf1是把一个两个参数普通函数的第一个参数绑定为10,生成了一个新的一个参数的可调用实体体;bf2是把一个类成员函数绑定了类对象,生成了一个像普通函数一样的新的可调用实体;bf3是把类成员函数绑定了类对象和第二个参数,生成了一个新的std::function对象。看懂了上面的例子,下面我们来说说使用bind需要注意的一些事项:
(1)bind预先绑定的参数需要传具体的变量或值进去,对于预先绑定的参数,是pass-by-value的
(2)对于不事先绑定的参数,需要传std::placeholders进去,从_1开始,依次递增。placeholder是pass-by-reference的
(3)bind的返回值是可调用实体,可以直接赋给std::function对象
(4)对于绑定的指针、引用类型的参数,使用者需要保证在可调用实体调用之前,这些参数是可用的
(5)类的this可以通过对象或者指针来绑定
std::function
它是函数、函数对象、函数指针、和成员函数的包装器,可以容纳任何类型的函数对象,函数指针,引用函数,成员函数的指针。
以统一的方式处理函数、函数对象、函数指针、和成员函数。允许保存和延迟执行函数。函数和成员函数作为function
function是一个拥有任何可以以"(...)"符号进行调用的值的类型。特别地,bind的返回结果可以赋值给function类型。function十分易于使用。(译注:更直观地,可以把function看成是一种表示函数的数据类型,就像函数对象一样。只不过普通的数据类型表示的是数据,function表示的是函数这个抽象概念。)例如:typedefstd::functionf;//构造一个函数对象,它能表示的是一个返回值为float,两个参数为int,int的函数 structint_div{//构造一个可以使用"()"进行调用的函数对象类型 floatoperator()(intx,inty)const{return((float)x)/y;}; }; voidHelloWorld::testing() { ff1=int_div();//赋值 autoresult3=f1(10,2); } 成员函数可被看做是带有额外参数的自由函数:
structint_div{//构造一个可以使用"()"进行调用的函数对象类型 floatoperator()(intx,inty)const{return((float)x)/y;}; intint_div_fun(intx){returnx;}; }; typedefstd::functionf_2; boolHelloWorld::init() { f_2f2=std::mem_fn(&int_div::int_div_fun);//指向成员函数 int_divint_div_object; intv=f2(&int_div_object,5);//在对象x上用参数5调用X::foo() std::function ff=std::bind(f2,&int_div_object,std::placeholders::_1);//f的第一个参数是&x v=ff(5);//调用x.foo(5) } ps:被vs2012的bug给坑了。因为看网上的代码于是刚开始第9行是这么写的:f_2f2=&int_div::int_div_fun;
然后就报错误:Error1errorC2664:'std::_Func_class<_Ret,_V0_t,_V1_t>::_Set':cannotconvertparameter1from'_Myimpl*'to'std::_Func_base<_Rx,_V0_t,_V1_t>*'
查了一下,vs2010没有这个编译错误,但是2012有。2012必须得加上std::mem_fn才能编译。
可以用function取代函数指针。因为它可以保存函数延迟执行,所以比较适合作为回调函数,也可以把它看做类似于c#中特殊的委托,只有一个成员的委托。
structint_div{//构造一个可以使用"()"进行调用的函数对象类型 floatoperator()(intx,inty)const{return((float)x)/y;}; intint_div_fun(intx){returnx;}; int_div(std::function&f):m_callback(f){}; voidNotify() { m_callback(); } std::function m_callback; }; function还可以作为函数入参,这样可以在函数外部控制函数的内部行为了,让我们的函数变得更加灵活。
voidFoo(intx,std::function&f) { if(x%2==0) f(x); } voidG(intx) { cout< c++11中推出function是为了泛化函数对象,函数指针,引用函数,成员函数的指针,让我们可以按更统一的方式写出更加泛化的代码;推出bind是为了替换和增强之前标准库的bind1st和bind2st,让我们的用起来更方便
到此这篇关于C++11std::function和std::bind使用详解的文章就介绍到这了,更多相关C++11std::function和std::bind内容请搜素毛票票以前的文章或下面相关文章,希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。