在便编译期间侦测可转换性和继承性
在C++中侦测转换能力的想法是:合并运用sizeof和重载函数,面对两个陌生的类型T和U, 我们想在编译期间发掘U是否继承自T,意味着不必使用dynamic_cast耗损执行期效率, 发掘这种继承关系, 靠的是用来侦没可转换性机制和威力惊人的sizeof操作符. sizeof用在任何表达式,可以感知重载、模板具现、转换规则以及任何可以发生在C++表达式身上的机制,并且会直接传回大小,不需拖到执行期评估。
#include <vector> #include <iostream> template <class T,class U> class Conversion{ typedef char Small; class Big{char dummy[16];}; static Small Test(U); static Big Test(...); static T Maker(); public: enum{exists=sizeof(Test(Maker()))==sizeof(Small)}; enum{exists=(exists&&Conversion<U,T>::exists)}; enum{sameType=false}; }; template <class T> class Conversion<T,T> { public: enum{exists=1,exists2Way=1,sameType=1}; }; class Base { char* member; }; class Super:public Base{ char* superMember; }; #define SUPERSUBCLASS(BASE, SUPER) ( (Conversion<const SUPER*, const BASE*>::exists ) && !(Conversion<const BASE*, const void*>::sameType ) ) #define SUPERSUBCLASS_STRICT(BASE, SUPER) ( SUPERSUBCLASS(BASE, SUPER) && !(Conversion<const BASE, const SUPER>::sameType) ) #int main(int argc, char* ardv[]) { using namespace std; cout<<Conversion<double,int>::exists<<"\t" <<Conversion<char,char*>::exists<<"\t" <<Conversion<size_t,vector<int> >::exists<<"\t" <<endl; cout<<"SUPERSUBCLASS(double, int) "<<SUPERSUBCLASS(double, int)<<"\n" <<"SUPERSUBCLASS(Base, Super) "<<SUPERSUBCLASS(Base, Super)<<"\n" <<"SUPERSUBCLASS(Super, Base) "<<SUPERSUBCLASS(Super, Base)<<"\n" <<"SUPERSUBCLASS(char, char*) "<<SUPERSUBCLASS(char, char*)<<"\n" <<"SUPERSUBCLASS(char *, void*) "<<SUPERSUBCLASS(char *, void*)<<"\n" <<"SUPERSUBCLASS(void *, void*) "<<SUPERSUBCLASS(void *, void*)<<"\n" <<"SUPERSUBCLASS(size_t, vector<int>) "<<SUPERSUBCLASS(size_t, vector<int>)<<"\n" <<endl; cout<<"SUPERSUBCLASS_STRICT(double, int) "<<SUPERSUBCLASS_STRICT(double, int)<<"\n" <<"SUPERSUBCLASS_STRICT(Base, Super) "<<SUPERSUBCLASS_STRICT(Base, Super)<<"\n" <<"SUPERSUBCLASS_STRICT(Super, Base) "<<SUPERSUBCLASS_STRICT(Super, Base)<<"\n" <<"SUPERSUBCLASS_STRICT(char, char*) "<<SUPERSUBCLASS_STRICT(char, char*)<<"\n" <<"SUPERSUBCLASS_STRICT(char *, void*) "<<SUPERSUBCLASS_STRICT(char *, void*)<<"\n" <<"SUPERSUBCLASS_STRICT(void *, void*) "<<SUPERSUBCLASS_STRICT(void *, void*)<<"\n" <<"SUPERSUBCLASS_STRICT(size_t, vector<int>) "<<SUPERSUBCLASS_STRICT(size_t, vector<int>)<<"\n" <<endl; return 0; }
根据示例,我们需要分析编译期间侦测可转换型和继承性两个方面
(一)首先进行编译期间可转换性的分析
(1)我们提供两个重载函数
Small Test(U) //该重载函数接受转换目标的U类型
Big Test(...) //接受任何其他类型
我们以类型T的临时对象来调用这些重载函数,接受U的那个函数Small Test(U)被调用,我们就知道类型T可以转换为类型U;否则如果调用Big 则类型T无法转换为U类型。为知道那个重载函数被调用,我们队重载函数的返回值定义了不同的大小和类型,并以sizeof来区分大小,其中,类型本省无关紧要,重要的是他们的大小必须保证不同,这里用Small和Big满足约束条件,在C++中调用本实例的重载函数Big Test的结果会如何并不知道,但是我们此处不真正调用这个重载函数,它甚至没有被实例化,因为sizeof并不对它求值。
我们传一个对象给Teat(),并使用sizeof在函数的返回值,代码可以写成const bool convExists= ( sizeof(Test(T())) == sizeof(Small) );由于类型T可能将自己的默认构造函数设为private,此时T()会编译失败,由于sizeof不会对任何表达式求值,此处我们使用的解决方法是strawman function方法返回一个T类型对象,即实现如下代码
T MakeT();
enum { exists = sizeof(Test(MakeT())) == sizeof(Small)};
此处需要注意的是,像MakeT()和Test()不只没做任何事情,甚至根本没有被实例化,根本不真正存在。
(2)在类型T和类型U之间可能存在双向转换或者相同类型。示例中我们使用exists2Way 表示类型T和类型U之间是否可以双向转换,如果exists2Way = true表示可以双向转换;使用sameType表示类型T和类型U是否相同类型,如果sameType = true表示是相同类型。在如下代码部分是我们的功能代码
template <class T, class U> class Conversion { ... enum { exists2Way = (exists && Conversion<U, T>::exists) }; enum { sameType = false}; };
我们同时需要使用模板partial spacialization显示实例化Conversion来实现sameType = true的相同类型版本,如下功能代码
template <class T> class Conversion<T, T> { public: enum { exists = 1, exists2Way = 1, sameType = 1}; };
类型相同肯定存在双向转换,这样我们就成功实现功能代码。
(3)编译期间可转换性的输出结果的分析,相关的输出结果是:1 0 0 ; 这个结果是由代码
int main(int argc, char* argv[]) { using namespace std; cout<<Conversion<double, int>::exists<<"\t" <<Conversion<char, char*>::exists<<"\t" <<Conversion<size_t, vector<int> >::exists<<"\t" <<endl; ... }
(二)类型的继承性分析,在C++,子类的指针可以安全的转换到基类指针
... #define SUPERSUBCLASS(BASE, SUPER) ( (Conversion<const SUPER*, const BASE*>::exists ) && !(Conversion<const BASE*, const void*>::sameType ) ) #define SUPERSUBCLASS_STRICT(BASE, SUPER) ( SUPERSUBCLASS(BASE, SUPER) && !(Conversion<const BASE, const SUPER>::sameType) ) ... cout<<"SUPERSUBCLASS(double, int) "<<SUPERSUBCLASS(double, int)<<"\n" <<"SUPERSUBCLASS(Base, Super) "<<SUPERSUBCLASS(Base, Super)<<"\n" <<"SUPERSUBCLASS(Super, Base) "<<SUPERSUBCLASS(Super, Base)<<"\n" <<"SUPERSUBCLASS(char, char*) "<<SUPERSUBCLASS(char, char*)<<"\n" <<"SUPERSUBCLASS(char *, void*) "<<SUPERSUBCLASS(char *, void*)<<"\n" <<"SUPERSUBCLASS(void *, void*) "<<SUPERSUBCLASS(void *, void*)<<"\n" <<"SUPERSUBCLASS(size_t, vector<int>) "<<SUPERSUBCLASS(size_t, vector<int>)<<"\n" <<endl; cout<<"SUPERSUBCLASS_STRICT(double, int) "<<SUPERSUBCLASS_STRICT(double, int)<<"\n" <<"SUPERSUBCLASS_STRICT(Base, Super) "<<SUPERSUBCLASS_STRICT(Base, Super)<<"\n" <<"SUPERSUBCLASS_STRICT(Super, Base) "<<SUPERSUBCLASS_STRICT(Super, Base)<<"\n" <<"SUPERSUBCLASS_STRICT(char, char*) "<<SUPERSUBCLASS_STRICT(char, char*)<<"\n" <<"SUPERSUBCLASS_STRICT(char *, void*) "<<SUPERSUBCLASS_STRICT(char *, void*)<<"\n" <<"SUPERSUBCLASS_STRICT(void *, void*) "<<SUPERSUBCLASS_STRICT(void *, void*)<<"\n" <<"SUPERSUBCLASS_STRICT(size_t, vector<int>) "<<SUPERSUBCLASS_STRICT(size_t, vector<int>)<<"\n" <<endl; ...
实现两个类型的继承判断,输出结果为:
SUPERSUBCLASS(double, int) 0 SUPERSUBCLASS(Base, Super) 1 SUPERSUBCLASS(Super, Base) 0 SUPERSUBCLASS(char, char*) 0 SUPERSUBCLASS(char *, void*) 0 SUPERSUBCLASS(void *, void*) 1 SUPERSUBCLASS(size_t, vector<int>) 0 SUPERSUBCLASS_STRICT(double, int) 0 SUPERSUBCLASS_STRICT(Base, Super) 1 SUPERSUBCLASS_STRICT(Super, Base) 0 SUPERSUBCLASS_STRICT(char, char*) 0 SUPERSUBCLASS_STRICT(char *, void*) 0 SUPERSUBCLASS_STRICT(void *, void*) 0 SUPERSUBCLASS_STRICT(size_t, vector<int>) 0
根据结果可以知道,版本SUPERSUBCLASS(BASE, SUPER)在类型U是public继承自类型T,或者类型T和类型U是同一类型时,返回true.当SUPERSUBCLASS(BASE, SUPER)对const U*和const T*做"可转换性"评估时, 只有如下三种情况const U*可以隐式转换成const T*:
1. T和U是同一类型。
2. T是U的一个unambiguous(非歧义的) public base.
3. T是void
版本SUPERSUBCLASS_STRICT(BASE, SUPER)能够更好的区别出继承关系。其中我们在代码前都加上const 是因为template代码实施const两次,第二个会被忽略,这样就不会因为const而导致转型失败。
在使用以上判定继承性的时候,我们只能判定以public方式继承的子类,这是一个比较遗憾的缺陷。同时声明了explicit 默认构造函数的类型,也会对断定方法造成很大的影响,因为protected 或者private的默认构造函数,可能会造成sizeof操作符 can not access member或者inaccessible.