您的位置:首页 > 编程语言 > C语言/C++

我的C++实践(12):函数指针与仿函数

2016-07-29 00:00 435 查看
C++的函数调用语法实体有函数、类似于函数的宏、函数指针、仿函数(即函数对象)。函数调用的方式有:
(1)直接调用:通过函数名直接调用函数,函数起始地址成为指令的一部分,因此编译期就能确定调用了哪个函数。
(2)间接调用:通过函数指针来调用函数,函数起始地址位于内存或寄存器的某处,因此到运行期才能确定调用哪个函数。
(3)内联调用:直接在调用处展开函数代码,在编译期进行,因此到运行期就不存在函数调用了。
对函数指针和仿函数的使用,基本上都是某种形式的回调。通常的程序库是库作者来编写库的代码,让用户来使用这些库,而回调正好相反,程序库调用了某个函数,而这个函数的代码需要由使用库的用户来编写。在实际应用中,仿函数与函数指针相比有很多优点,因为仿函数是类对象,因此我们可以在仿函数中关联状态信息(当然这会使仿函数类占内存空间,但我们可以使用空基类优化技术),这样根据不同的参数生成不同的函数实例。仿函数可以作为模板的类型实参来传递,也可以作为函数调用实参来传递。但仿函数是类对象,因此不能作为非类型模板实参,但函数指针可以作为非模板模板实参。通过仿函数,我们可以把所有的的普通函数都封装成仿函数类,这样就可以以对象的方式来统一使用各个函数。我们也可以对仿函数的各个参数类型和返回类型进行萃取,从而使其具有反射的能力,不同的仿函数还可以方便地组合起来使用。
编写仿函数类的基本约定: 在仿函数类中要定义仿函数的参数个数常量NumParams、每个参数的类型typedef Param1T,typedef Param2T,...、返回类型tyepdef ReturnT。根据约定,我们的仿函数类应该编写成类似于下面的形式:

class PersonSort{
public:
enum(NumParams=2 };
typedef bool ReturnT;
typedef Person const& Param1T;
typedef Person const& Param2T;
bool operator()(Person const& p1,Person const& p2) const{
//返回p1是否小于p2
}
};


1、仿函数的类型萃取实现: FunctorParam<F,N>用来获取仿函数F的第N的参数的类型,当N值不大于实际的参数个数时,调用UsedFunctorParam<F,N>模板针对N的特化来获取相应位置处的参数类型,当N值大于实际的参数个数时,返回私有的无用嵌套成员类 NNest,它不能用来创建对象,因此没有副作用。代码如下:

//functorparam.hpp:获取仿函数各个参数的类型
#ifndef FUNCTOR_PARAM_HPP
#define FUNCTOR_PARAM_HPP
#include "ifthenelse.hpp"  //用到了IfThenElse模板
/*
* UsedFuctorParam<F,N>:抽取仿函数F的实际使用的第N个参数类型
*/
template <typename F, int N>
class UsedFunctorParam; //基本模板
//对每个N值,都提供一个局部特化,以获取相应位置处参数的类型
//使用宏FunctorParamSpec(N)来编写UsedFunctorParam的特化,可以使代码更简洁
#define FunctorParamSpec(N)                 /
template<typename F>                        /
class UsedFunctorParam<F, N>{               /
public:                                     /
typedef typename F::Param##N##T Type;   /
};
//假设N到20为止,很少有多于20个参数的仿函数
FunctorParamSpec(1)
FunctorParamSpec(2)
FunctorParamSpec(3)
FunctorParamSpec(4)
FunctorParamSpec(5)
FunctorParamSpec(6)
FunctorParamSpec(7)
FunctorParamSpec(8)
FunctorParamSpec(9)
FunctorParamSpec(10)
FunctorParamSpec(11)
FunctorParamSpec(12)
FunctorParamSpec(13)
FunctorParamSpec(14)
FunctorParamSpec(15)
FunctorParamSpec(16)
FunctorParamSpec(17)
FunctorParamSpec(18)
FunctorParamSpec(19)
FunctorParamSpec(20)
#undef FunctorParamSpec  //宏FunctorParamSpec的生命期结束
/*
* FunctorParam<F,N>::Type:获取仿函数F的第N个参数的类型,当N大于实际参数
* 个数F::NumParams时,FunctorParam<F,N>::Type会返回私有的无用嵌套类
* NNest,它不能用来创建对象,因此没有副作用
*/
template <typename F, int N>
class FunctorParam{
private:
class Unused{ //嵌套类
private:
class NNest {}; //嵌套的无用成员类
public:
typedef NNest Type;
};
public:
//获取相应位置处的参数类型
typedef typename IfThenElse<F::NumParams>=N,
UsedFunctorParam<F,N>,
Unused>::ResultT::Type
Type;
};
#endif


解释:
(1)UsedFuctorParam<F,N>用来抽取仿函数F的实际使用的第N个参数类型,这里N没有超过仿函数的实际参数个数。参数类型的抽取是通过对每个N值提供一个局部特化来实现的。假设仿函数的参数个数最多为20,对具有工业强度的库而言,其设计一般要达到20个参数左右。实际中很少有多于20个参数的仿函数。这里#undef表示宏FunctorParamSpec的生命期结束,即下面就已经不存在FunctorParamSpec这个宏了,要用的话需要重新定义。
(2)FunctorParam<F,N>中,N可以超过仿函数的实际参数个数,当超过时,就返回一个私有的无用嵌套类NNest,NNest在FunctorParam内部,在外部并不能直接用类名NNest来创建对象,因此对不存在的参数虽然返回了一个类型,但并没有产生副作用。这里用到了类型萃取技术中开发的IfThenElse模板。
2、把函数指针封装成仿函数: 直接对函数指针的返回类型和各个参数类型进行参数化,设计成仿函数类模板。
下面的ForwardParamT<T>用于决定仿函数的参数类型T是传引用还是传原来的普通类型,T为类类型时则传引用,其他情况则传原来的普通类型(这需要用到类型萃取技术中开发的TypeOp模板),这样可以避免昂贵的拷贝构造函数调用。对仿函数参数为空(即为void)的情况,提供一个全局特化,指定空参数的类型为一个无副作用的哑类型。

//forwardparam.hpp:ForwardParamT<T>用于决定仿函数的参数类型T是传引用还是传原来的普通类型
#ifndef FORWARD_PARAM_HPP
#define FORWARD_PARAM_HPP
#include "ifthenelse.hpp"
#include "isclasst.hpp"
#include "typeop.hpp"    //用于决定函数的参数是传引用还是传原来的类型
template<typename T>
class ForwardParamT{ //基本模板:T为类类型则传const引用,否则就传普通类型
public:
typedef typename IfThenElse<IsClassT<T>::Yes,
typename TypeOp<T>::RefConstT,
typename TypeOp<T>::ArgT
>::ResultT
Type;
};
template<>
class ForwardParamT<void>{ //全局特化:void表示函数参数为空,则传哑类型Ununsed,
private:                   //它不能创建对象,因此没有副作用
class Unused {};
public:
typedef Unused Type;
};
#endif


下面的FunctionPtrT用来根据函数指针的返回类型和各个参数类型构造出函数指针(这里针对最多为5个参数的情况,对含有更多个参数的函数原理和做法是一样的)。最后的FunctionPtr就是真正封装函数指针的仿函数类型,要遵循仿函数的编写约定。它组合一个用FunctionPtrT构造出来的函数指针,然后根据函数指针的不同实参个数(0-5个)重载operator(),并用函数指针来进行调用实际的函数即可。在调用函数时,用到了上面的ForwardParamT<T>,这样就可以在传递调用实参时避免拷贝构造函数的调用。包装函数func_ptr(*fp)用来方便地创建封装了函数指针的函数对象。

//functionptr.hpp:把最多为5个参数的函数指针类型封装成仿函数模板FunctionPtr<RT,P1,P2,P3,P4,P5>
//对含有更多个参数的函数,其原理和做法一样的
#ifndef FUNCTION_PTR_HPP
#define FUNCTION_PTR_HPP
#include "forwardparam.hpp"  //用于决定函数的形参T是传引用还是传原来的普通类型
/*
* FunctionPtrT<RT,P1,P2,P3,P4,P5>:用来根据返回类型和各个参数类型构造出函数指针
*/
template<typename RT, typename P1 = void,
typename P2 = void,
typename P3 = void,
typename P4 = void,
typename P5 = void>
class FunctionPtrT{ //基本模板:函数指针有5个参数的情况
public:
enum { NumParams = 5 }; //定义参数个数的枚举量
typedef RT (*Type)(P1,P2,P3,P4,P5); //根据参数类型来定义函数指针
};
template<typename RT, typename P1,
typename P2,
typename P3,
typename P4>
class FunctionPtrT<RT,P1,P2,P3,P4,void>{ //局部特化:4个参数的情况
public:
enum { NumParams = 4 };
typedef RT (*Type)(P1,P2,P3,P4);
};
template<typename RT, typename P1,
typename P2,
typename P3>
class FunctionPtrT<RT,P1,P2,P3,void,void>{ //局部特化:3个参数的情况
public:
enum { NumParams = 3 };
typedef RT (*Type)(P1,P2,P3);
};
template<typename RT, typename P1,
typename P2>
class FunctionPtrT<RT,P1,P2,void,void,void>{ //局部特化:2个参数的情况
public:
enum { NumParams = 2 };
typedef RT (*Type)(P1,P2);
};
template<typename RT, typename P1>
class FunctionPtrT<RT,P1,void,void,void,void>{ //局部特化:1个参数的情况
public:
enum { NumParams = 1 };
typedef RT (*Type)(P1);
};
template<typename RT>
class FunctionPtrT<RT,void,void,void,void,void>{ //局部特化:0个参数的情况
public:
enum { NumParams = 0 };
typedef RT (*Type)();
};
/*
* FunctionPtr<RT,P1,P2,P3,P4,P5>:封装函数指针的仿函数类型
* 使用缺省的void时表示该参数为空
*/
template<typename RT, typename P1 = void,
typename P2 = void,
typename P3 = void,
typename P4 = void,
typename P5 = void>
class FunctionPtr{
private:
//获得相应的函数指针类型
typedef typename FunctionPtrT<RT,P1,P2,P3,P4,P5>::Type FuncPtr;
FuncPtr fptr; //被封装的函数指针
public:
//保存好参数个数及其各个参数类型
enum { NumParams = FunctionPtrT<RT,P1,P2,P3,P4,P5>::NumParams };
typedef RT ReturnT;
typedef P1 Param1T;
typedef P2 Param2T;
typedef P3 Param3T;
typedef P4 Param4T;
typedef P5 Param5T;
FunctionPtr(FuncPtr ptr):fptr(ptr) { //构造函数,创建函数对象,它封装了函数指针
}
// 下面是函数调用
RT operator()() { //0个参数的情况
return fptr();
}
RT operator()(typename ForwardParamT<P1>::Type a1){ //1个参数的情况
return fptr(a1);
}
RT operator()(typename ForwardParamT<P1>::Type a1,
typename ForwardParamT<P2>::Type a2){ //2个参数的情况
return fptr(a1, a2);
}
RT operator()(typename ForwardParamT<P1>::Type a1,
typename ForwardParamT<P2>::Type a2,
typename ForwardParamT<P3>::Type a3){ //3个参数的情况
return fptr(a1, a2, a3);
}
RT operator()(typename ForwardParamT<P1>::Type a1,
typename ForwardParamT<P2>::Type a2,
typename ForwardParamT<P3>::Type a3,
typename ForwardParamT<P4>::Type a4){ //4个参数的情况
return fptr(a1,a2,a3,a4);
}
RT operator()(typename ForwardParamT<P1>::Type a1,
typename ForwardParamT<P2>::Type a2,
typename ForwardParamT<P3>::Type a3,
typename ForwardParamT<P4>::Type a4,
typename ForwardParamT<P5>::Type a5){ //5个参数的情况
return fptr(a1,a2,a3,a4,a5);
}
};
/*
* func_ptr(*fp):包装函数,用于方便地创建封装了函数指针的函数对象
*/
template<typename RT>
inline FunctionPtr<RT> func_ptr(RT (*fp)()){ //0个参数的情况
return FunctionPtr<RT>(fp);
}
template<typename RT, typename P1>
inline FunctionPtr<RT,P1> func_ptr(RT (*fp)(P1)){ //1个参数的情况
return FunctionPtr<RT,P1>(fp);
}
template<typename RT, typename P1, typename P2>
inline FunctionPtr<RT,P1,P2> func_ptr(RT (*fp)(P1,P2)){ //2个参数的情况
return FunctionPtr<RT,P1,P2>(fp);
}
template<typename RT, typename P1, typename P2, typename P3>
inline FunctionPtr<RT,P1,P2,P3> func_ptr(RT (*fp)(P1,P2,P3)){ //3个参数的情况
return FunctionPtr<RT,P1,P2,P3>(fp);
}
template<typename RT, typename P1, typename P2, typename P3,typename P4>
inline FunctionPtr<RT,P1,P2,P3,P4> func_ptr(RT (*fp)(P1,P2,P3,P4)){ //4个参数的情况
return FunctionPtr<RT,P1,P2,P3,P4>(fp);
}
template<typename RT, typename P1, typename P2, typename P3,typename P4,typename P5>
inline FunctionPtr<RT,P1,P2,P3,P4,P5> func_ptr(RT (*fp)(P1,P2,P3,P4,P5)){ //5个参数的情况
return FunctionPtr<RT,P1,P2,P3,P4,P5>(fp);
}
#endif


测试代码如下:

//functionptrtest.cpp:对封装函数指针的仿函数进行测试
#include <iostream>
#include <string>
#include <typeinfo>
#include "functionptr.hpp"
double seven(){ //0个参数的情况
return 7.0;
}
std::string more(){ //0个参数的情况
return std::string("more");
}
int display(int a,int b){ //2个参数的情况
std::cout<<a<<","<<b<<std::endl;
return 0;
}
int output(int a,double b,std::string c){ //3个参数的情况
std::cout<<a<<","<<b<<","<<c<<std::endl;
return 0;
}
template <typename FunctorT>
void demo(FunctorT func){ //func是封装了函数指针的函数对象,这个函数指针有0个参数
std::cout << "Functor returns type: "
<< typeid(typename FunctorT::ReturnT).name() << '/n'
<< "Functor returns value: "
<< func() << '/n';
}
int main(){
demo(func_ptr(seven));
demo(func_ptr(more));
func_ptr(display)(123,456);
func_ptr(output)(83,3.14159,"Jack Zhou");
return 0;
}


3、仿函数的组合: Composer<FO1,FO2>用来组合两个仿函数的使用,先调用仿函数FO1(其参数个数可以是0-20个),再把其返回的结果的用作实参来调用仿函数FO2。可见FO2只能有一个参数,且与FO1的返回类型相同,这样才能组合使用。包装函数compose(f1,f2)用来方便地创建两个仿函数的组合对象。

//composer.hpp:Composer<FO1,FO2>用于组合两个仿函数的使用
#ifndef COMPOSER_HPP
#define COMPOSER_HPP
#include "forwardparam.hpp"  //用于决定函数的形参T是传引用还是传原来的普通类型
#include "functorparam.hpp"  //用于获取仿函数各个参数的类型
/*
* BaseMem<C,N>:为了避免Composer多次继承同一个类而引入的间接层
*/
template <typename C, int N>
class BaseMem : public C { //直接继承模板参数C(是一个仿函数类)
public:
BaseMem(C& c) : C(c) { }
BaseMem(C const& c) : C(c) { }
};
/*
* Composer<FO1,FO2>:组合两个仿函数FO1、FO2,先调用FO1,再调用FO2
* 通过BaseMem间接继承FO1和FO2,这样当FO1或FO2为空类是就可以进行空基类优化
*/
template <typename FO1, typename FO2>
class Composer : private BaseMem<FO1,1>,
private BaseMem<FO2,2>{
public:
enum { NumParams = FO1::NumParams }; //参数个数
typedef typename FO2::ReturnT ReturnT; //最后的返回类型
//用FunctorParam来获取仿函数FO1的各个参数类型,
//作为Composer的各个参数Param1T, Param2T,...
// 用宏来简化参数类型的定义
#define ComposeParamT(N)      /
typedef typename FunctorParam<FO1, N>::Type Param##N##T;

//假设N到20为止,很少有多于20个参数的仿函数
ComposeParamT(1)
ComposeParamT(2)
ComposeParamT(3)
//...
ComposeParamT(20)
#undef ComposeParamT
//构造函数:组合两个仿函数对象,这里考虑了各种const和非const的情况
//把两个仿函数对象存储在基类子对象中,这就允许进行空基类优化
Composer(FO1 const& f1, FO2 const& f2)
: BaseMem<FO1,1>(f1), BaseMem<FO2,2>(f2) {
}
Composer(FO1 const& f1, FO2& f2)
: BaseMem<FO1,1>(f1), BaseMem<FO2,2>(f2) {
}
Composer(FO1& f1, FO2 const& f2)
: BaseMem<FO1,1>(f1), BaseMem<FO2,2>(f2) {
}
Composer(FO1& f1, FO2& f2)
: BaseMem<FO1,1>(f1), BaseMem<FO2,2>(f2) {
}
//针对0个参数的函数调用
ReturnT operator() (){ //针对0个参数的函数调用
return BaseMem<FO2,2>::operator()(BaseMem<FO1,1>::operator()());
}
//针对1个参数的函数调用
ReturnT operator() (typename ForwardParamT<Param1T>::Type v1) {
return BaseMem<FO2,2>::operator()(BaseMem<FO1,1>::operator()(v1));
}
//针对2个参数的函数调用
ReturnT operator() (typename ForwardParamT<Param1T>::Type v1,
typename ForwardParamT<Param2T>::Type v2) {
return BaseMem<FO2,2>::operator()(BaseMem<FO1,1>::operator()(v1, v2));
}
//针对3个参数的函数调用
ReturnT operator() (typename ForwardParamT<Param1T>::Type v1,
typename ForwardParamT<Param2T>::Type v2,
typename ForwardParamT<Param3T>::Type v3) {
return BaseMem<FO2,2>::operator()(BaseMem<FO1,1>::operator()(v1, v2,v3));
}

//...
//一直写到针对20个参数的函数调用
};
/*
* compose(f1,f2)包装函数:用于方便地创建两个仿函数的组合
*/
template <typename FO1, typename FO2>
inline Composer<FO1,FO2> compose(FO1 f1, FO2 f2) {
return Composer<FO1,FO2> (f1, f2);
}
#endif


//composertest.cpp:对仿函数组合的测试
#include <iostream>
#include "composer.hpp"
#include "functionptr.hpp"
double add(double a, double b){
return a+b;
}
double twice(double a){
return 2*a;
}
int main(){
std::cout << "compute (20+7)*2: "
<< compose(func_ptr(add),func_ptr(twice))(20,7)
<< '/n';
return 0;
}


解释:
(1)这里让Composer<FO1,FO2>通过BaseMem间接继承模板参数FO1,FO2,以便当FO1和FO2为空类时可以进行空基类优化。如果Composer直接继承FO1和FO2,当FO1和FO2为同一个类型时,编译会出错,因此我们需要要引入间接层BaseMem,现在的基类BaseMem<FO1,1>和BaseMem<FO2,2>并不是同一个类型。
(2)Composer是仿函数类,需要遵循仿函数的编写约定。它的各个参数类型是FO1的各个参数类型。因此我们需要用FunctorParam来抽取仿函数FO1的各个参数类型,以作为Composer的各个参数Param1T, Param2T,...,Param20T。
(3)构造函数用于组合两个仿函数对象,它需要考虑两个仿函数的const和非const的情况。下面的函数调用运算符中是先调用FO1,把返回结果作为实参再调用FO2。
4、仿函数参数的值绑定: 即将仿函数的某个参数绑定为一个特定的值,这样仿函数的参数就可以减少一个。我们需要开发出一个绑定器(是一个仿函数)来作绑定操作,一个独立的类来存放绑定值。绑定器的各个参数类型萃取也比较麻烦,它需要去掉仿函数中被绑定的参数,然后后面的参数又要前移一个位置,因此我们把绑定器的参数类型萃取抽离出来设计成一个独立的模板。
下面的BoundVal<T>用于在运行期存放T类型的绑定值(这个值后面会被绑定到仿函数的某个参数上去),而StaticBoundVal<T> 则用于在编译期存放T类型的绑定值。

//boundval.hpp:存放绑定值的模板,可通过get()来获取这个绑定值
#ifndef BOUND_VAL_HPP
#define BOUND_VAL_HPP
#include "typeop.hpp"
template <typename T>
class BoundVal{ //在运行期存放T类型的绑定值
private:
T value; //存放绑定值的对象
public:
typedef T ValueT;
BoundVal(T v):value(v){ //把要绑定的值通过构造函数传进来
}
typename TypeOp<T>::RefT get(){ //设置或获取绑定的值
return value;  //返回非const的引用
}
};
template <typename T, T Val>
class StaticBoundVal{ //在编译期存放T类型的绑定值
public:
typedef T ValueT;
T get(){ //通过get()来获取绑定的值
return Val;
}
};
#endif


下面的BinderParams<F,P>用来萃取绑定器的参数类型。它只要去掉仿函数中被绑定的参数, 并把后面的参数前移1个位置即可。

//binderparams.hpp:绑定器的参数类型列表,去掉仿函数中被绑定的参数,
//并把后面的参数前移1个位置即可
#ifndef BINDER_PARAMS_HPP
#define BINDER_PARAMS_HPP
#include "ifthenelse.hpp"
/*
* BinderParams<F,P>:用于将被绑定参数(即第P个参数)后面的参数前移1个位置
* BinderParams<F,P>::ParamNT:仿函数F被绑定后的第N个参数类型
*/
template<typename F, int P>
class BinderParams{
public:
enum { NumParams = F::NumParams-1 };//一个参数已绑定,故参数个数减1

//将被绑定参数(即第P个参数)后面的参数前移1个位置
#define ComposeParamT(N)                                   /
typedef typename IfThenElse<(N<P), FunctorParam<F, N>,     /
FunctorParam<F, N+1>    /
>::ResultT::Type                /
Param##N##T;

//假设N到20为止,很少有多于20个参数的仿函数
ComposeParamT(1)
ComposeParamT(2)
ComposeParamT(3)
//...
ComposeParamT(20)

#undef ComposeParamT
};
#endif


下面的SignSelectT<S,NegT,ZeroT,PosT>根据整数S是正、0、还是负来选择相应的类型。

//signselect.hpp:根据整数S是正、0、还是负来选择相应的类型
#ifndef SIGN_SELECT_HPP
#define SIGN_SELECT_HPP
#include "ifthenelse.hpp"
template <int S, typename NegT, typename ZeroT, typename PosT>
struct SignSelectT{
typedef typename
IfThenElse<(S<0),
NegT,
typename IfThenElse<(S>0),
PosT,
ZeroT
>::ResultT
>::ResultT
ResultT;
};
#endif


下面是真正的绑定器Binder<FO,P,V>。它把仿函数FO的第P个参数绑定为一个存放在V类型对象中的绑定值,绑定后它还要完成对仿函数FO的调用,即Binder<FO,P,V>本身就相当于被绑定后的FO。Binder的参数类型列表Params是仿函数FO去掉绑定参数后的各个参数类型,可见Binder::operator()的参数个数比底层的FO::operator()少一个,因此它在调用底层的FO::operator()时需要重新确定原来FO的参数,并指定一个参数为绑定值。嵌套类模板ArgSelect<A>就是用来确定FO的各个实参。包装函数bind<P>(fo,val)用于方便地将仿函数对象fo的第P个参数绑定为值val。

//binder.hpp:用于对仿函数的参数进行值绑定
#ifndef BINDER_HPP
#define BINDER_HPP
#include "ifthenelse.hpp"
#include "typeop.hpp"
#include "boundval.hpp"     //用于存放要绑定的值
#include "forwardparam.hpp" //用于决定仿函数的形参T是传引用还是传原来的普通类型
#include "functorparam.hpp" //获取仿函数各个参数的类
#include "binderparams.hpp" //用于将被绑定参数后面的参数前移1个位置
#include "signselect.hpp"   //根据整数的正负性来选择相应的类型
/*
* Binder<FO,P,V>:把仿函数FO的第P个参数绑定为一个存放在V类型对象中的绑定值,
* V是BoundVal<T>或StaticBoundVal<T,val>类型,里面存放了真正
* 的绑定值(是T类型的)
*/
template <typename FO, int P, typename V>
class Binder : private FO, private V{
public:
enum { NumParams = FO::NumParams-1 }; //一个参数已经绑定,故参数个数减1
typedef typename FO::ReturnT ReturnT; //返回类型
typedef BinderParams<FO, P> Params; //参数类型列表:Params中含有仿函数FO
//去掉绑定参数后的各个参数类型

//定义Binder需要的各个参数类型(或者为原来类型的const引用,或者为原来的普通类型)
#define ComposeParamT(N)                                           /
typedef typename ForwardParamT<typename Params::Param##N##T>::Type /
Param##N##T;
ComposeParamT(1)
ComposeParamT(2)
ComposeParamT(3)
//...
ComposeParamT(20)

#undef ComposeParamT
//构造函数:根据传过来的仿函数对象和存放了绑定值的V类型对象构造Binder,
//这里考虑了各种const和非const的情况
Binder(FO& f): FO(f) {}
Binder(FO& f, V& v): FO(f), V(v) {}
Binder(FO& f, V const& v): FO(f), V(v) {}
Binder(FO const& f): FO(f) {}
Binder(FO const& f, V& v): FO(f), V(v) {}
Binder(FO const& f, V const& v): FO(f), V(v) {}

//构造函数模板:直接根据仿函数对象和要绑定的值(T类型的)构造Binder,绑定的值存储在
//BoundVal<T>中,即在运行期存储绑定值
template<class T>
Binder(FO& f, T& v): FO(f), V(BoundVal<T>(v)) {}

template<class T>
Binder(FO& f, T const& v): FO(f), V(BoundVal<T const>(v)) {}
//函数调用:对调用实参个数不同的情况,都要重载调用运算符
//Binder::operator()的参数个数比底层的FO::operator()少一个
ReturnT operator()(){
return FO::operator()(V::get());
}
ReturnT operator()(Param1T v1){
//根据实参位置(1和2)与P的相对位置,用from函数自动选择是使用用户
//提供的v1,还是使用被绑定的值V::get()
return FO::operator()(ArgSelect<1>::from(v1,v1,V::get()),
ArgSelect<2>::from(v1,v1,V::get()));
}
ReturnT operator()(Param1T v1, Param2T v2){
return FO::operator()(ArgSelect<1>::from(v1,v1,V::get()),
ArgSelect<2>::from(v1,v2,V::get()),
ArgSelect<3>::from(v2,v2,V::get()));
}
ReturnT operator()(Param1T v1, Param2T v2, Param3T v3) {
return FO::operator()(ArgSelect<1>::from(v1,v1,V::get()),
ArgSelect<2>::from(v1,v2,V::get()),
ArgSelect<3>::from(v2,v3,V::get()),
ArgSelect<4>::from(v3,v3,V::get()));
}
//...
//一直写到针对20个参数的函数调用
private:
template<int A>
class ArgSelect{ //私有的嵌套类模板:实参选择器,用来确定FO原来的各个实参
public:
//定义绑定实参前面的类型:FO的第A个参数类型ArgSelect<A>::NoSkipT恰为
//Binder的第A个参数类型(取其引用类型),当A达到比NumParams多1时,退回到Binder的A-1处
typedef typename TypeOp<
typename IfThenElse<(A<=Params::NumParams),
FunctorParam<Params, A>,
FunctorParam<Params, A-1>
>::ResultT::Type>::RefT
NoSkipT;

//定义绑定实参后面的类型:FO的第A个参数类型ArgSelect<A>::SkipT恰为
//Binder的第A-1个参数类型(取其引用类型),当A为1时,前移到Bindder的A处
//ArgSelect<A>::NoSkipT
typedef typename TypeOp<
typename IfThenElse<(A>1),
FunctorParam<Params, A-1>,
FunctorParam<Params, A>
>::ResultT::Type>::RefT
SkipT;

//定义绑定实参处的类型:在存放绑定值的V类型中有记录
typedef typename TypeOp<typename V::ValueT>::RefT BindT;
//3个不同的嵌套类,用来实现3种情况下FO实参值的选择
class NoSkip{ //选择绑定参数前面的实参:为用户指定的实参
public:
static NoSkipT select(SkipT prev_arg, NoSkipT arg,
BindT bound_val) {
return arg;
}
};
class Skip{ //选择绑定参数后面的实参:为用户指定的实参
public:
static SkipT select(SkipT prev_arg, NoSkipT arg,
BindT bound_val){
return prev_arg;
}
};
class Bind{ //选择绑定参数处的实参:为绑定值
public:
static BindT select(SkipT prev_arg, NoSkipT arg,
BindT bound_val){
return bound_val;
}
};
//根据A与P的相对位置返回仿函数FO中A处的参数类型
typedef typename SignSelectT<A-P, NoSkipT,
BindT, SkipT>::ResultT
ReturnT;

//根据A与P的相对位置返回仿函数FO的实参选择类(上面的3个嵌套类之一,用来
//选择FO在A处的实参值)
typedef typename SignSelectT<A-P, NoSkip,
Bind, Skip>::ResultT
SelectedT;

//实际的选择函数:
static ReturnT from (SkipT prev_arg, NoSkipT arg,
BindT bound_val){
//根据获得的选择类,调用select来选择相应的实参值
return SelectedT::select(prev_arg, arg, bound_val);
}
};
};
/*
* 包装函数bind<P>(fo,val):将仿函数对象fo的第P个参数绑定为值val
* FO可演绎出来,FunctorParam<FO,P>::Type为绑定值的类型
* BoundVal<typename FunctorParam<FO,P>::Type>为存放绑定值的V类型
* ForwardParamT可用来决定val是传引用还是传原来的类型
*/
template <int P,typename FO>     //P为绑定参数的位置,FO为仿函数
inline Binder<FO,P,BoundVal<typename FunctorParam<FO,P>::Type> >
bind(FO const& fo,
typename ForwardParamT<typename FunctorParam<FO,P>::Type>::Type val){
//返回一个对fo进行绑定后的Binder仿函数对象
return Binder<FO,
P,
BoundVal<typename FunctorParam<FO,P>::Type>
>(fo,BoundVal<typename FunctorParam<FO,P>::Type>(val));
}
#endif


解释:
(1)Binder继承自模板参数FO和V,这样就可以使用空基类优化技术。Binder是一个仿函数,因此它要遵循仿函数的编写约定,定义参数个数和各个参数类型(用上面的BinderParams来萃取)。
(2)构造函数根据传过来的仿函数对象和存放了绑定值的V类型对象构造Binder,它需要考虑各种const和非const的情况。而构造函数模板则直接根据仿函数对象和要绑定的值(T类型的)构造Binder,绑定的值存储在BoundVal<T>中,即在运行期存储绑定值。
(3)ArgSelect<A>用来确定原来FO的各个实参,这样才能在Binder中完成对被绑定的FO的调用。它先根据A的位置定义绑定实参前面的类型NoSkipT、绑定实参后面的类型SkipT、以及绑定实参处的类型BindT。然后定义3个不同的嵌套类,里面有一个静态的select函数,用来实现3种情况下对FO实参值的选择。接着根据A与P的相对位置,用SignSelectT得到仿函数FO中A处的参数类型、以及仿函数FO的实参选择类(即上面的的3个嵌套类之一)。最后是真正的实参选择函数from,它调用相应选择类的select,根据实参位置A与P的相对位置,自动选择FO中A处是使用用户提供的实参,还是使用被绑定的值。
(4)函数调用Binder::operator()对调用实参个数不同的情况,都要进行重载。Binder::operator()直接调用FO::operator(),而各个实参是根据所在位置用ArgSelect<n>::from来自动选择,对FO中绑定处的参数用绑值V::get(),对其他位置的参数则用用户传递过来的实参。
(5)包装函数bind<P>(fo,val)直接返回一个对fo绑定后的少了一个参数的仿函数对象。它的使用就相当于对少了一个参数的fo的使用。
测试代码如下:

//bindertest.cpp:对绑定器Binder的测试
#include <string>
#include <iostream>
#include "functionptr.hpp"
#include "binder.hpp"
//这个函数会被func_ptr封装为仿函数
bool func(std::string const& str, double d, float f){
std::cout << str << ": "
<< d << (d<f? "<": ">=")
<< f << '/n';
return d<f;
}
int main(){
//把仿函数func_ptr(func)的第1个参数绑定为"Comparing"
bool result = bind<1>(func_ptr(func), "Comparing")(1.0, 2.0);
std::cout << "bound function returned " << result << '/n';
return 0;
}


5、普通函数参数的值绑定: 可先用上面的func_ptr(*fp)把函数(最多只能接受5个参数)封装成仿函数,再用bind来进行值绑定即可。上面的测试代码中就是这样用的。从中我们可以看出,通过使用func_ptr和bind的实现,我们可以对任意普通函数的参数进行值绑定。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: