zoukankan      html  css  js  c++  java
  • C++中的函数指针模板

    所谓函数指针模板,就是指向函数模板的函数指针,也可以称为泛型函数指针。

    问题描述:定义了一类函数模板,而且这类函数模板有共同的接口,即一致的参数列表。那么如何定义一个函数指针,使这个函数指针可以指向这一类中的所有函数模板呢?


    一、先我们应当明确一点,在C++中,模板函数仅仅是一个用来生成函数的代码块,它本身是没有实体的,也就没有与“未被实例化的那些代码”相对应的程序代码块,所以也就无法对其取地址(不存在的东西,怎么会有具体的内存地址呢?)。只有在用具体类型代替模板参数,对该模板进行实例化以后才能有函数实体。

    而函数指针要指向函数的入口地址,那么既然函数模板没有具体的内存地址,那么指向函数模板的函数指针如何得到地址呢?所以所谓的“函数模板指针”这个定义是无法通过以下的方法实现的:

    template<class T> void (*sample)(T &);
    

       


    二、尽管无法通过这个定义实现所谓的“函数模板指针”,但是并不代表C++无法解决这个问题。使用模板类与模板函数一起,可以有一种对此问题的解决方案。举例如下,(VS2010编译通过):

    #include <iostream>
    using namespace std;
    class CA{
    public:    
        int Sum(int a, int b)
    	{
            return a + b;
        }
    };
    
    class CB{
    public:
        float Sum(float a, float b)
    	{
            return a + b;
        }
    };
    
    template <typename ClassType, typename ParaType>
    class CC{
    public:
        /*函数指针类型模板*/
        typedef ParaType (ClassType::*pClassFun)(ParaType, ParaType);
    
        /*函数指针函数模块*/
        ParaType Result(ClassType* pClassType, pClassFun fun, ParaType a, ParaType b)
        {
            return (pClassType->*fun)(a, b);
        }
    
    };
    
    void main(){
        /*测试整型*/
        CA ca;
        CC<CA, int> cc;
        int a = 3;
        int b = 4;
    	cout<<"The sum of a and b is "<<cc.Result(&ca, &CA::Sum, a, b)<<endl;
    
        /*测试浮点型*/
        CB cb;
        CC<CB, float> fcc;
        float fa = 3.3f;
        float fb = 4.6f;
    	cout<<"The sum of fa and fb is "<<fcc.Result(&cb, &CB::Sum, fa, fb)<<endl;
    }
    

      

    解释:

    1、第23行:需要使用typedef关键字,使编译器明白pClassFun是一个函数指针类型名(不是函数指针),这个类型的函数指针都可以指向一个函数,被指向的这个函数必须满足以下条件:返回值为ParaType、参数列表为(ParaType, ParaType)、而且是一个定义在ClassType类中的成员函数。注意:需要在作用域标示符后,函数指针类型名之前,一定要加上*(dereference),表明要定义的是函数指针类型。如果不加这个操作符,这部分就变成了对一个名叫pClassFun的成员函数进行的泛化了,那么typedef关键字的存在似乎就是没有意义的了。

    2、第28行:使用这个函数指针时,需要创建一个类的实例,对函数指针fun所指向的成员函数,需要由实例来完成调用。

    3、第39、46行:第二个参数需要在传入时,在参数之前加上引用操作符(取地址)。一般来说函数名就是函数的入口地址(即指针),但也许是VS的规定(此处还不是很清楚),需要使用 &ClassType::MemberFunction 这样的形式,才能创建指向成员函数的指针(error C3867: “CA::Sum”: 函数调用缺少参数列表;请使用“&CA::Sum”创建指向成员的指针)。

    4、我们可以看到,由于使用泛型(即模板类)无法确定函数的入口地址,所以无法直接在函数指针上使用泛型。但是经过模板类的包装,我们就可以在类的内部使用泛型函数指针。因为类成员函数存在动态绑定技术,或者说,在加载函数时,模板类中泛型的实际类型对于模板函数来说可以说是已知的。另外,在返回函数指针时,我们多加了一层包装,形式上有些类似于C#中的代理(delegate),或者可以说,这是一种适配器模式。于是,所谓的“函数指针模板”就这样实现了。


    三、如果我们定义的是包含在一组工具类中的一系列工具方法,那么一般来说,这些工具方法往往都是类的静态成员函数。静态成员函数的访问与调用,是通过ClassType::StaticMemberFunction 来实现的。对照上一部分中的解释2,工具类与工具方法最主要的特点,就是不需要实例化对象就可以直接使用方法。那么这种情况下的“函数指针模板”要如何实现呢?对上一部分中的代码稍作修改即可,举例如下(VS2010编译通过):

    #include <iostream>
    using namespace std;
    class CA{
    public:    
        static int Sum(int a, int b);
    };
    int CA::Sum(int a, int b)
    {
    	return a + b;
    }
    
    class CB{
    public:
        static float Sum(float a, float b);
    };
    float CB::Sum(float a, float b)
    {
        return a + b;
    }
    
    template <typename ParaType>
    class CC{
    public:
        /*函数指针类型模板*/
        typedef ParaType (*pClassFun)(ParaType, ParaType);
    
        /*函数指针函数模块*/
        ParaType Result(pClassFun fun, ParaType a, ParaType b)
        {
            return (*fun)(a, b);
        }
    };
    
    void main(){
        /*测试整型*/
        CC<int> cc;
        int a = 3;
        int b = 4;
    	cout << "The sum of a and b is " << cc.Result(CA::Sum, a, b) << endl;
    
        /*测试浮点型*/
        CC<float> fcc;
        float fa = 3.3f;
        float fb = 4.6f;
    	cout << "The sum of fa and fb is " << fcc.Result(CB::Sum, fa, fb) << endl;
    }
    

      

    解释:

    1、由于静态成员函数定义在类的外边,从易于理解的角度来说,我们可以把它们当作全局函数来调用。所以第25行的typedef类型声明和第30行函数指针的调用,都比上面的例子简单了很多。这里没有了复杂的作用域控制符等等限制,更接近于常见的普通函数指针,只不过在使用泛型时不能直接进行声明,依然是必须嵌套在模板类内部,使用模板类传来已确定的泛型类型来实现“函数指针模板”。

    2、第39、45行在获取类的静态成员函数的入口地址时,只需要使用 ClassType::StaticMemberFunction 的形式即可获得函数的入口地址,完全符合”函数名即函数入口地址“的准则。在这里再看一下,上例中获取非静态成员函数入口地址的方式 &ClassType::MemberFunction,即一定要加上引用操作符才行。

    四、现在我们借助C++ 11的新特性,即auto关键字和decltype关键字,就可以直接实现“函数指针模板”这个形式了。实现需要混合使用auto和decltype关键字,这也是引入decltype的一个原因——让C++有能力写一个 “ 转发函数模板”(出处:http://coolshell.cn/articles/5265.html)。

    template< typename LHS, typename RHS>
    auto AddingFunc(const LHS &lhs, const RHS &rhs) -> decltype(lhs+rhs)
    {return lhs + rhs;}
    

      这个函数模板看起来相当费解,用到了auto 和 decltype 来扩展了已有的模板技术的不足,在上例中,我不知道AddingFunc会接收什么样类型的对象,这两个对象的+操作符返回的类型也不知道,所以使用老的模板函数无法定义AddingFunc返回值和这两个对象相加后的返回值匹配,所以,你可以使用上述的这种定义。

    参考:

    1、http://bbs.csdn.net/topics/310122062

    2、http://www.cnblogs.com/xianyunhe/archive/2011/11/27/2265148.html

    3、http://www.cnblogs.com/xianyunhe/archive/2011/11/26/2264709.html

    4、http://blog.csdn.net/eclipser1987/article/details/5666220

  • 相关阅读:
    快速幂
    hdu 1595 find the longest of the shortest(迪杰斯特拉,减去一条边,求最大最短路)
    hdu 4121 Xiangqi
    hdu 2224 The shortest path
    hdu4923
    矩阵模板
    hdu4570(区间dp)
    hdu1978(记忆化搜索)
    hdu4283(区间dp)
    hdu1160最长递减子序列及其路径
  • 原文地址:https://www.cnblogs.com/superpig0501/p/3967576.html
Copyright © 2011-2022 走看看