首页 > 代码库 > C++模板:辨别函数类型

C++模板:辨别函数类型

在《C++ Template》中有一个辨识函数类型的模板技术,原文的例子貌似有些错误,这里做个对比验证,如有错误,请大家指出,原文的代码如下:

template<typename T>
class CompoundT {
public:
	enum { IsPtrT = 0, IsRefT = 0, IsArrayT = 0, IsFuncT = 0, IsPtrMemT = 0 };
	typedef T BaseT;
	typedef T BottomT;
	typedef CompoundT<void> ClassT;
};

这是基本模板,接着需要定义一个基本模板的特化,用于识别函数类型:

template<typename T>
class CompoundT<T()> {
public:
	enum { IsPtrT = 0, IsRefT = 0, IsArrayT = 0, IsFuncT = 1, IsPtrMemT = 0 };
	typedef T BaseT();
	typedef T BottomT();
	typedef CompoundT<void> ClassT;
};

这个经过特化的类模板可以识别无参的函数类型,为了灵活使用,原文通过一个模板函数来完成具体的识别工作,如下:
template<typename T>
void test(T){
	cout<<typeid(T).name()<<endl;
	cout<<(CompoundT<T>::IsFuncT)<<endl;
}
这个模板函数到底能不能工作呢,我们可以实地验证一下:
void fun(){}

int main(){

	test(fun);
	return 0;
}

发现输出的结果如下:

typeid(T).name() : PFvvE

CompoundT<T>:: IsFuncT : 0

显然,识别的结果是错误的,问题是出在test的定义上,注意到test的参数列表时(T),也就是说采用了传值方式,而如果传递的是函数,会发生decay,即将函数名称decay为函数指针,所以 T 被推断成了函数指针类型,而不是函数类型,所以上面定义的特化版本无法与之匹配。现在将test重新定义如下:(将T改成T&)

template<typename T>
void test(T&){
	cout<<typeid(T).name()<<endl;
	cout<<(CompoundT<T>::IsFuncT)<<endl;
}
再次执行相同的测试,结果如下:

typeid(T).name() : FvvE

CompoundT<T>:: IsFuncT : 1

可见,这样就可以正确识别函数类型了。此外,我们也可以看到模板函数与模板类结合使用的巨大优势,因为模板函数可以进行类型推断(类型演绎)。如果不使用函数模板,CompoundT的作用就大大下降了,因为不能写成CompoundT<fun>,因为fun不是一个类型,fun相当于类型的实例。为了更加具体地说明这一点,我们可以进行如下定义:

typedef void func();
这样就可以使用CompoundT<func>了,因为func现在是一个函数类型。



C++模板:辨别函数类型