裹一层重载的成员函数指针

使用变长参数模板和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);// call A::func(int)
void(A::* AfuncFloat)(float)=&A::func;
(aobj.*AfuncFloat)(11.11);// call A::func(float)

由于类型系统不能表示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;
};
// direct call
(aobj.*(genLambda(int{})))(11);
// bind
auto AfunInt=bind(genLambda(int{}),aobj,_1);
Afun(11);//call A::func(int)

但是感觉还是有点繁琐,干脆做成一个辅助函数,第一个模板参数接受类类型,第二个模板参数接收函数的参数类型:

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
// direct call
(aobj,*(AfuncArgType<A,int>()))(10);//call A::func(int)
// bind
auto AfuncInt=bind(AfuncArgType<A,int>(),aobj,_1);
AfuncInt(11);// call A::func(int);
auto AfuncFloat=bind(AfuncArgType<A,float>(),aobj,_1);
AfuncFloat(11.11);// call A::func(float)

本来打算把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); // call A::func(int,float)
auto AfuncIntFloatBool=bind(AfuncArgType<A,int,int,bool>(),aobj,_1,_2,_3);
AfuncIntFloatBool(10,11.11,true); // call A::func(int,float.bool)

对比一下如果直接硬怼的代码:

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);
全文完,若有不足之处请评论指正。
本文标题:裹一层重载的成员函数指针
文章作者:ZhaLiPeng
发布时间:2016年12月31日 04时02分
更新时间:2017年05月15日 16时27分
本文字数:本文一共有859字
原始链接:https://imzlp.me/posts/19740/
许可协议: CC BY-NC-SA 4.0
转载请保留原文链接及作者信息,谢谢!
您的捐赠将鼓励我继续创作!