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

免责声明:文章转载自《C++中的函数指针模板》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇oracle 报错 :ORA-04052、 ORA-00604、 ORA-03106、 ORA-02063海康、大华IPC的rtsp格式下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

为枚举类型添加描述信息 this 扩展 泛型约束 位运算[转]

为枚举类型添加描述信息 this 扩展 泛型约束 位运算 2011年10月13日 星期四 上午 10:09     在开发应用中,我们经常用枚举来简化程序。但是让人头的是总得枚举一个别名Alias用于显示或者描述该枚举值,这时候如果我们采用if或者switch的方法来进行判读也可以,但是有点不够优雅。下面来给大家分享一下我的实现方法。今天同事把博客园里的...

Java虚拟机14:Java对象大小、对象内存布局及锁状态变化

一个对象占多少字节? 关于对象的大小,对于C/C++来说,都是有sizeof函数可以直接获取的,但是Java似乎没有这样的方法。不过还好,在JDK1.5之后引入了Instrumentation类,这个类提供了计算对象内存占用量的方法。至于具体Instrumentation类怎么用就不说了,可以参看这篇文章如何精确地测量java对象的大小。 不过有一点不同的...

Unity3D游戏轻量级xlua热修复框架

一  这是什么东西 前阵子刚刚集成xlua到项目,目的只有一个:对线上游戏C#逻辑有Bug的地方执行修复,通过考察xlua和tolua,最终选择了xlua,很大部分原因是因为项目已经到了后期,线上版本迭代了好几次,所以引入Lua的目的不是为了开发新版本模块。xlua在我们的这种情况下很是适用,如xlua作者所说,用C#开发,用lua热更,xlua这套框架为...

C 语言函数指针

c代码: #include <stdio.h> int add(int x,int y); int subtract(int x,int y); int domath(int (*mathop)(int,int),int x,int y); int add(int x,int y) { return x+y; } int subtra...

OVER(PARTITION BY)函数介绍

2010年10月26日---将B栏位值相同的对应的C 栏位值加总select a,b,c, SUM(C) OVER (PARTITION BY B) C_Sumfrom test A B C C_SUM 1 1 1 1 1 2 2 7 2 2 5 7 1 3 3 3 3 4 6 6 ---如果不需要已某个栏位的值分割,那就要用 null eg: 就是将C...

spark SQL之 DataFrame使用

pom.xml中 <!-- https://mvnrepository.com/artifact/com.google.guava/guava --> <dependency> <groupId>com.google.guava</groupId> <artifactId...