使用变长参数模板和lambda(或者使用generic lambda)来裹一层重载的成员函数指针,从而方便使用(bind绑定或者其他需要重载的成员函数指针的地方)。 关于成员函数指针 的更多介绍请看我的另一篇文章:C++中指向类成员的指针并非指针 。
注意:代码有些用到了C++14的特性,编译时需加上-std=c++14
参数。
当前有一个类,重载了几个成员函数,如下:
1 2 3 4 5 6 7 8 9 struct A { void func (int x) { cout<<"A::func(int)" <<endl; } void func (float x) { cout<<"A::func(float)" <<endl; } };
通常我们可以使用下面的方法得到成员函数指针:
1 2 3 4 5 A aobj; void (A::* AfuncInt)(int )=&A::func;(aobj.*AfuncInt)(11 ); void (A::* AfuncFloat)(float )=&A::func;(aobj.*AfuncFloat)(11.11 );
由于类型系统不能表示candidate set,如果我们想要bind到这个类的成员函数func,只能手动指定重载的参数:
1 2 auto x=bind ((void (A::*)(float ))&A::func,aobj,_1);auto y=bind ((void (A::*)(int ))&A::func,aobj,_1);
这样太麻烦了,可以使用generic lambda
来裹一下:
1 2 3 4 5 6 7 8 auto genLambda=[](auto argType)->auto { return (void (A::*)(decltype (argType)))&A::func; }; (aobj.*(genLambda (int {})))(11 ); auto AfunInt=bind (genLambda (int {}),aobj,_1);Afun (11 );
但是感觉还是有点繁琐,干脆做成一个辅助函数,第一个模板参数接受类类型,第二个模板参数接收函数的参数类型:
1 2 3 4 5 6 7 template <typename T,typename U>auto AfuncArgType (void ) { auto argType=[](void )->auto { return (void (T::*)(U))&A::func; }; return argType (); }
然后就可以这样调用:
1 2 3 4 5 6 7 (aobj,*(AfuncArgType <A,int >()))(10 ); auto AfuncInt=bind (AfuncArgType <A,int >(),aobj,_1);AfuncInt (11 );auto AfuncFloat=bind (AfuncArgType <A,float >(),aobj,_1);AfuncFloat (11.11 );
本来打算把bind的也裹进去,但是在placeholders
名字空间中的_1
…_n
这些在重载的函数参数个数不同时就不方便指定了。
而且上面的代码还有一个局限性:只能指定一个参数的函数,如果我重载了多个参数的版本,则上面的代码是行不通的。
但是我们使用可变参数模板 可以实现支持多个模板参数,因为至少要指定类类型,所以要有两个模板参数,T作为类类型,Args作为A类的函数成员的参数类型:
1 2 3 4 5 6 7 template <typename T,typename ... Args>auto AfuncArgType (void ) { auto argType=[](void )->auto { return (void (T::*)(Args...))&T::func; }; return argType (); }
这样我们就可以绑定多个参数了,假如我们在之前的类的基础上又重载了两个多参数的版本:
1 2 3 4 5 6 7 8 9 10 struct A { void func (int x,float y) { cout<<"func(int,float)" <<endl; } void func (int x,int y,bool z) { cout<<"func(int,int,bool)" <<endl; } };
我们可以使用上面可变模板参数的版本:
1 2 3 4 auto AfuncIntFloat=bind (AfuncArgType <A,int ,float >(),aobj,_1,_2);AfuncIntFloat (10 ,11.11 ); auto AfuncIntFloatBool=bind (AfuncArgType <A,int ,int ,bool >(),aobj,_1,_2,_3);AfuncIntFloatBool (10 ,11.11 ,true );
对比一下如果直接硬怼的代码:
1 2 3 4 5 6 auto a=bind ((void (A::*)(float ))&A::func,aobj,_1);a (11 );auto b=bind ((void (A::*)(int ,float ))&A::func,aobj,_1,_2);b (11 ,12.11 );auto c=bind ((void (A::*)(int ,int ,bool ))&A::func,aobj,_1,_2,_3);c (11 ,12.11 ,true );