分类: C/C++
2013-02-18 14:57:39
在 C++ 中“侦测转换能力”的想法是:合并运用sizeof和重载函数。面对两个陌生的类型T和U, 我们想在编译期间发掘U是否继承自T,意味着不必使用dynamic_cast耗损执行期效率, 发掘这种继承关系, 靠的是用来侦没可转换性机制和威力惊人的sizeof操作符. sizeof用在任何表达式,可以感知重载、模板具现、转换规则以及任何可以发生在C++表达式身上的机制,并且会直接传回大小,不需拖到执行期评估。(内容源自书籍 mordern C++ design:generic programming and design patterns applied)。我们使用如下测试代码。 (如果存在继承关系,T代表base, U代码super)
#include#include template class Conversion { typedef char Small; class Big{char dummy[16];}; static Small Test(U); static Big Test(...); static T MakeT(); public: enum { exists = sizeof(Test(MakeT())) == sizeof(Small) }; enum { exists2Way = (exists && Conversion::exists) }; enum { sameType = false}; }; template class Conversion { public: enum { exists = 1, exists2Way = 1, sameType = 1}; }; class Base { char * member; }; class Super : public Base { char * superMember; }; #define SUPERSUBCLASS(BASE, SUPER) ( (Conversion ::exists ) && !(Conversion ::sameType ) ) #define SUPERSUBCLASS_STRICT(BASE, SUPER) ( SUPERSUBCLASS(BASE, SUPER) && !(Conversion ::sameType) ) int main(int argc, char* argv[]) { using namespace std; cout< ::exists<<"\t" < ::exists<<"\t" < >::exists<<"\t" < ) "< )<<"\n" < ) "< )<<"\n" < 输出的结果为: 1 0 0 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) 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 ) 0 根据示例,我们需要分析编译期间侦测可转换性和继承性两个方面。
(一)首先进行编译期间可转换性的分析。
(1)我们提供两个重载函数:
Small Test(U); //该重载函数接受转换目标的U类型
Big Test(...); //接受任何其他类型
我们以类型T的临时对象来调用这些重载函数,如果接受U的那个函数"Small Test(U)"被调用,我们就知道类型T可以转换为U类型;否则如果调用"Big Test(..)"则T类型就无法转换成U类型。为知道哪个重载函数被调用,我们对重载函数的返回值安排了不同的大小和类型,并用sizeof来区分大小。其中,类型本身无关紧要,重要的是它们的大小必须保证不同,我们使用的Small和Big肯定满足这个要求。在C++中,调用本示例的重载函数"Big Test(...)"的结果会如何并不能知道,但是我们此处不真正调用这个重载函数,它甚至没有被实例化,因为sizeof并不对它求值。
我们传一个T对象给Test(), 并使用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。我们同时需要使用模板partial spacialization显示实例化Conversion来实现sameType = true的相同类型版本,如下功能代码class Conversion { ... enum { exists2Way = (exists && Conversion::exists) }; enum { sameType = false}; }; template。类型相同肯定存在双向转换,这样我们就成功实现功能代码。class Conversion { public: enum { exists = 1, exists2Way = 1, sameType = 1}; };
(3)编译期间可转换性的输出结果的分析,相关的输出结果是:1 0 0 ; 这个结果是由代码
int main(int argc, char* argv[]) { using namespace std; cout<::exists<<"\t" < ::exists<<"\t" < >::exists<<"\t" < 输出的。结果很显然。特别需要注意的是,在输出结果中,代码"<
>"输出的是0,因为vector的构造函数是explicit,explicit构造函数是无法担任转换函数的。 (二)类型的继承性分析。在C++中,子类的指针可以安全的转换到基类指针, 示例中使用代码
... #define SUPERSUBCLASS(BASE, SUPER) ( (Conversion::exists ) && !(Conversion ::sameType ) ) #define SUPERSUBCLASS_STRICT(BASE, SUPER) ( SUPERSUBCLASS(BASE, SUPER) && !(Conversion ::sameType) ) ... cout<<"SUPERSUBCLASS(double, int) "< ) "< )<<"\n" < ) "< )<<"\n" < 实现两个类型的继承判断,输出结果为:
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) 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 ) 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.