我的C++实践(9):一个完整的类型区分框架
(1)辨别基本类型:IsFundaT
独特构造:基本类型都是已知的,并且个数有限,因此直接用特化来实现。定义一个基本模板表示非基本类型,然后结合宏MK_FUNDA_TYPE(T)来对各个基本类型提供特化,以表示它们是基本类型,用宏主要是为了使代码更简洁。
//isfundat.hpp:辨别基本类型
#ifndef IS_FUNDAT_HPP
#define IS_FUNDAT_HPP
template
class IsFundaT { //基本模板:表示非基本类型
public:enum { Yes = 0, No = 1};
};
// 用于特化基本类型的宏
#define MK_FUNDA_TYPE(T) /
template<> class IsFundaT { /
public: /enum { Yes = 1, No = 0 }; /
};
MK_FUNDA_TYPE(void)
MK_FUNDA_TYPE(bool)
MK_FUNDA_TYPE(char)
MK_FUNDA_TYPE(signed char)
MK_FUNDA_TYPE(unsigned char)
MK_FUNDA_TYPE(wchar_t)
MK_FUNDA_TYPE(signed short)
MK_FUNDA_TYPE(unsigned short)
MK_FUNDA_TYPE(signed int)
MK_FUNDA_TYPE(unsigned int)
MK_FUNDA_TYPE(signed long)
MK_FUNDA_TYPE(unsigned long)
#if LONGLONG_EXISTS //如果编译器支持long long类型MK_FUNDA_TYPE(signed long long)MK_FUNDA_TYPE(unsigned long long)
#endif
MK_FUNDA_TYPE(float)
MK_FUNDA_TYPE(double)
MK_FUNDA_TYPE(long double)
#undef MK_FUNDA_TYPE
#endif (2)辨别函数类型:IsFunctionT
独特构造:数组元素的类型不能为void类型、引用类型或函数类型。因此可构造一个测试用的成员函数模板,函数形参为模板参数的数组类型指针,这样数组元素就不能接受函数类型。同时提供该成员另一个重载版本,以应付T是函数类型的情况(SFINAE原则),对有干扰的void类型和引用类型提供特化以表示它们不是函数类型,这样就可以辨别T了。
//isfunctiont.hpp:辨别函数类型
#ifndef IS_FUNCTIONT_HPP
#define IS_FUNCTIONT_HPP
template
class IsFunctionT {
private:typedef char One;typedef struct { char a[2]; } Two;template static One test(...); //U为函数时使用这个,只需声明,无需定义template static Two test(U (*)[1]); //U为非函数、非引用及非void类型时使用这个
public:enum { Yes = sizeof(test(0)) == 1 }; //记录测试的结果enum { No = !Yes };
};
template
class IsFunctionT { //T是引用类型时会使用这个局部特化,表示它不是函数类型
public:enum { Yes = 0 };enum { No = !Yes };
};
template<>
class IsFunctionT { //T是void类型时会使用这个全局特化,表示它不是函数类型
public:enum { Yes = 0 };enum { No = !Yes };
};
template<>
class IsFunctionT { //T是void const类型时会使用这个全局特化
public:enum { Yes = 0 };enum { No = !Yes };
};
// 对于void volatile和void const volatile类型也是一样
//...
#endif 代码中的U (*)[1]是一个指向数组U[1]的指针,U是数组元素的类型。可见这里的U不能为函数、引用及void类型。当U为函数、引用及void以外的其他类型时,sizeof就会使用这个版本的test函数,返回的Two的字节数2,则Yes为0,表示非函数类型。当为引用或void类型时,会使用特化版本,设置Yes为0。当为函数类型时,sizeof中的Test匹配有数组指针的那个Test时不会成功,但它能匹配另一个返回One为1个字节的test版本,根据SFINAE原则,它会使用这个版本的test,Yes设置为1。注意这里test只需声明,无需定义,因为sizeof并不会真正调用并执行该函数,不需要函数代码定义,它只是计算返回类型的字节数。
当然,我们还可以用其他的独特构造来实现,比如只有对函数类型,F&(指向函数类型的引用)才能转化为F*。通过判断能否把一个F&转化为F*,也可辨别出F是否是函数类型。
(3)辨别复合类型:CompoundT
独特构造:这些类型局部已知,且个数有限,可直接用局部特化来实现。对函数类型则可复用上面的IsFunctionT
//compoundt.hpp:辨别复合类型,这包括指针类型、引用类型、数组类型、函数类型、成员指针类型
#ifndef COMPOUNDT_HPP
#define COMPOUNDT_HPP
#include
#include "isfunctiont.hpp"
template
class CompoundT { //基本模板
public:enum { IsPtrT = 0, IsRefT = 0, IsArrayT = 0,IsFuncT = IsFunctionT::Yes,IsPtrMemT = 0 };typedef T BaseT; //T直接依赖的类型typedef T BottomT; //去除所有复合依赖后最底层的那个裸类型typedef CompoundT ClassT; //T所属的类(用在成员指针类型中)
};
template
class CompoundT { //对引用类型的局部特化
public:enum { IsPtrT = 0, IsRefT = 1, IsArrayT = 0,IsFuncT = 0, IsPtrMemT = 0 };typedef T BaseT;typedef typename CompoundT::BottomT BottomT;typedef CompoundT ClassT;
};
template
class CompoundT { //对指针类型的局部特化
public:enum { IsPtrT = 1, IsRefT = 0, IsArrayT = 0,IsFuncT = 0, IsPtrMemT = 0 };typedef T BaseT;typedef typename CompoundT::BottomT BottomT;typedef CompoundT ClassT;
};template
class CompoundT { //对数组类型的局部特化
public:enum { IsPtrT = 0, IsRefT = 0, IsArrayT = 1,IsFuncT = 0, IsPtrMemT = 0 };typedef T BaseT;typedef typename CompoundT::BottomT BottomT;typedef CompoundT ClassT;
};
template
class CompoundT { //对空数组类型的局部特化
public:enum { IsPtrT = 0, IsRefT = 0, IsArrayT = 1,IsFuncT = 0, IsPtrMemT = 0 };typedef T BaseT;typedef typename CompoundT::BottomT BottomT;typedef CompoundT ClassT;
};
template
class CompoundT { //对成员指针类型的局部特化
public:enum { IsPtrT = 0, IsRefT = 0, IsArrayT = 0,IsFuncT = 0, IsPtrMemT = 1 };typedef T BaseT;typedef typename CompoundT::BottomT BottomT;typedef C ClassT;
};
//
//下面几个是针对给定参数个数的函数类型局部特化,不提供也没问题,因为上面使用了IsFunctionT
//提供了还可以访问它们的返回类型和参数类型
template
class CompoundT { //对无参数的函数类型的局部特化,R是函数的返回类型
public:enum { IsPtrT = 0, IsRefT = 0, IsArrayT = 0,IsFuncT = 1, IsPtrMemT = 0 };typedef R BaseT();typedef R BottomT();typedef CompoundT ClassT;
};
template
class CompoundT { //对单参数的函数类型的局部特化
public: //R是返回类型,P1是参数类型enum { IsPtrT = 0, IsRefT = 0, IsArrayT = 0,IsFuncT = 1, IsPtrMemT = 0 };typedef R BaseT(P1);typedef R BottomT(P1);typedef CompoundT ClassT;
};
template
class CompoundT { //对不少于一个参数的函数类型的局部特化
public:enum { IsPtrT = 0, IsRefT = 0, IsArrayT = 0,IsFuncT = 1, IsPtrMemT = 0 };typedef R BaseT(P1);typedef R BottomT(P1);typedef CompoundT ClassT;
};
//还可以定义其他给定参数个数的函数类型局部特化#endif 这里还对给定参数个数的函数类型定义了局部特化。这些特化不提供也没问题,因为代码中是使用上面的IsFunctionT
(4)辨别类类型(class,struct,union):IsClassT
独特构造:只有类类型才有成员指针。因此可构造一个测试用的成员函数模板,函数形参为模板参数的一个成员指针,这样模板参数就只能为类类型了。同时提供该成员另一个重载版本,以应付T是非类类型时的情况(SFINAE原则)。
//isclasst.hpp:辨别类类型(class,struct,union)
#ifndef IS_CLASST_HPP
#define IS_CLASST_HPP
template
class IsClassT{ //确定某个类型是否为类类型
private:typedef char One;typedef struct{char a[2];} Two;templatestatic One test(int C::*); //C是类类型时使用这个版本,函数参数是一个C的成员指针templatestatic Two test(...); //C是非类类型时使用这个版本
public:enum { Yes=sizeof(test(0))==1 }; //是类类型则Yes为1,不是类类型时Yes为0enum { No=!Yes };
};
#endif (5)辨别枚举类型。IsEnumT
独特构造:枚举类型可以隐式转型为整型,这需要排除其他能够转型为整型的基本类型、指针类型、引用类型、成员指针类型的干扰。可用特化,也可用重载解析来实现。定义一个可转型为模板参数的类模板,和一个测试用的函数,为各种受干扰的类型提供该函数的重载版本。通过传递一个可转型的对象给测试函数,转型为模板参数T后,就会运用重载解析来调用相应的重载版本,以辨别出枚举类型。
//isenumt.hpp:辨别枚举类型,运用类型转换和重载解析来实现
#ifndef IS_ENUMT_HPP
#define IS_ENUMT_HPP
#include "isfundat.hpp"
#include "compoundt.hpp"
struct SizeOverOne { char c[2]; }; //用作返回类型
template::IsFuncT &&!CompoundT::IsArrayT>
class ConsumeUDC { //基本模板:T为非函数类型非数组类型时,ConsumeUDC允许转型为Tpublic:operator T() const; //转型运算符:转型为T
};
template
class ConsumeUDC {//对非函数类型非数组类型的特化:ConsumeUDC不允许转型为T
};
template
class ConsumeUDC { //对void类型的特化:不允许转型
};
//下面的enum_check函数只需声明,无需定义,因为sizeof运算并不会真正调用该函数
char enum_check(bool);
char enum_check(char);
char enum_check(signed char);
char enum_check(unsigned char);
char enum_check(wchar_t);
char enum_check(signed short);
char enum_check(unsigned short);
char enum_check(signed int);
char enum_check(unsigned int);
char enum_check(signed long);
char enum_check(unsigned long);
#if LONGLONG_EXISTS //如果编译器支持long long类型char enum_check(signed long long);char enum_check(unsigned long long);
#endif
//避免从float到int的意外转型
char enum_check(float);
char enum_check(double);
char enum_check(long double);
SizeOverOne enum_check(...); //捕获剩余的所有情况
template
class IsEnumT { //辨别枚举类型public:enum { Yes = IsFundaT::No &&!CompoundT::IsRefT &&!CompoundT::IsPtrT &&!CompoundT::IsPtrMemT &&sizeof(enum_check(ConsumeUDC()))==1 };enum { No = !Yes };
};
#endif IsEnumT
(5)完整的类型区分实现。
//typet.hpp:完整的类型区分实现
#ifndef TYPET_HPP
#define TYPET_HPP
#include "isfundat.hpp"
#include "compoundt.hpp"
#include "isclasst.hpp"
#include "isenumt.hpp"
template
class TypeT { //辨别所有类型的模板
public:enum { IsFundaT = IsFundaT::Yes,IsPtrT = CompoundT::IsPtrT,IsRefT = CompoundT::IsRefT,IsArrayT = CompoundT::IsArrayT,IsFuncT = CompoundT::IsFuncT,IsPtrMemT = CompoundT::IsPtrMemT,IsEnumT = IsEnumT::Yes,IsClassT = IsClassT::Yes };
};
#endif // TYPET_HPP (6)测试代码。
//typestest.cpp:类型区分的测试
#include "typet.hpp"
#include
class MyClass { //类类型
};
void myfunc(){ //函数类型
}
enum E { e1 }; //枚举类型
template
void check(){ //检查传进来的模板实参类型if (TypeT::IsFundaT) {std::cout << " IsFundaT ";}if (TypeT::IsPtrT) {std::cout << " IsPtrT ";}if (TypeT::IsRefT) {std::cout << " IsRefT ";}if (TypeT::IsArrayT) {std::cout << " IsArrayT ";}if (TypeT::IsFuncT) {std::cout << " IsFuncT ";}if (TypeT::IsPtrMemT) {std::cout << " IsPtrMemT ";}if (TypeT::IsEnumT) {std::cout << " IsEnumT ";}if (TypeT::IsClassT) {std::cout << " IsClassT ";}std::cout << std::endl;
}
template
void checkT (T a){ //检查传进来的函数调用实参类型check();//对指针类型,检查它们所依赖的类型if (TypeT::IsPtrT || TypeT::IsPtrMemT) {check::BaseT>(); //BaseT为T所依赖的类型}
}
int main(){std::cout << "int:" << std::endl;check();std::cout << "int&:" << std::endl;check();std::cout << "char[42]:" << std::endl;check();std::cout << "MyClass:" << std::endl;check();std::cout << "ptr to enum:" << std::endl;E* ptr = 0; //指向枚举类型的指针checkT(ptr);std::cout << "42:" << std::endl;checkT(42);std::cout << "myfunc():" << std::endl;checkT(myfunc); //myfunc会退化为指针,它所依赖的类型为函数类型void()std::cout << "memptr to array:" << std::endl;char (MyClass::* memptr) [] = 0; //指向char型空数组的成员指针checkT(memptr);return 0;
}
转载于:https://my.oschina.net/abcijkxyz/blog/722718
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
