编译期间侦测可转换性和继承性
如何发现类型A可以隐式转换成为类型B,归根结底都落在了函数形参的匹配上,无论是明白的函数调用或是operator =的使用。如果一个接受类型B的函数可以接受A,那就说明了问题。如何知道两者调用了相同的函数(运行期)或者说如何知道两者决定调用相同的函数(编译期)。在运行期检查的方式简直多如牛毛,但是如何在编译期就检查出来呢?这还不容易,就仅仅定义一个这个函数,如果可以转化就通过编译,如果不能转化就报错!如果无论如何都要能编译,并且能通过值来表示,又该怎么办?
只有另想办法了:如果函数的返回值类型匹配就知道的确调用了这个函数,但是返回的是值,不是类型,而编译期对变量是无法对付的,怎么办:sizeof可以解决,即使返回值是个值,sizeof都是在编译期就求出这个值占用的大小而不需要这个值真实存在。这样的话即使如果不能转化也要让别个有函数可以调用,这个不能转化时候要调用的函数又该如何写?...(这就是答案,匹配的最低级,全转型,最全一级,虽然template也全,但是恰恰相反template是全匹配,不需要转型的)。
这样只要保证两个重载函数的sizeof不等就可以区别出来了!以下是实作:
template<class From, class To> class Conversion { //手工定义两个size不同大小的类,以作为类型的区别 typedef char Small; struct Big { char unName[2]; }; //From要是是虚拟构造函数我也不怕,sizeof不需要我求值 static From MakeFrom(); //当可以转型时候调用的版本 static Small Test(To); //当不可以转型时候调用的版本 static Big Test(...); public: //可以被外界感知的可否转型的数值标识 enum {//可以转型 exist = sizeof(Test(MakeFrom())) == sizeof(Small), //可以相互转型 exist2Way = exist && Conversion<To, From>::exist, //exist2Way = Conversion<To, From>::exist2Way, //两者相同 sameType = 0 }; }; template<class From> class Conversion<From, From> {//偏特化版本,利用template的完全匹配原则 public: enum { exist = 1, exist2Way = 1, sameType = 1 }; };
测试代码:
#include <iostream> using std::cout; using std::endl; #include "Conversion.h" int main() { /* 可否转换:1 可否双向转换:1 是否同一类型:0 可否转换:0 可否双向转换:0 是否同一类型:0 可否转换:1 可否双向转换:1 是否同一类型:1 */ cout << "可否转换:" << Conversion<int, double>::exist << endl << "可否双向转换:" << Conversion<int, double>::exist2Way <<endl << "是否同一类型:" << Conversion<int, double>::sameType <<endl; cout << "可否转换:" << Conversion<int, void*>::exist << endl << "可否双向转换:" << Conversion<int, void*>::exist2Way <<endl << "是否同一类型:" << Conversion<int, void*>::sameType <<endl; cout << "可否转换:" << Conversion<int, int>::exist << endl << "可否双向转换:" << Conversion<int, int>::exist2Way <<endl << "是否同一类型:" << Conversion<int, int>::sameType <<endl; }
有了上面的基础再来研究,如何发现类A继承于B,那就得从继承的意义上说起了,A is a B,B可以使用的地方,A一定可以使用(就是里氏替换原则哇),至少两个使用上的体现:1、A一定具有B成员;2、如果B可以作为某一函数的传入参数,那么A一定也可以传入。至于1由于成员每个类不一致,排除;再看2,如果一个函数以B为参数,如果后者也可以传入就好了,但是可以隐式转换的类型也有这种性质,我们把类型变成类型的指针呢?!:B* 可以作为某一函数传入参数,A*也一定可以同样传入,另外就只有以下两个问题了(类型相同也可以叫继承吧)
1、那B是void怎么办?任何指针都可以隐式转换为void*,解决方法是如果B为void*判断为false;
2、那const A可以传入接受B的函数么?解决方法是全转换成为const
这样就完美了:
#define DERIVE_BASE_CLASS(DERIVE, BASE) \ unsigned(Conversion</*const*/ DERIVE*, const BASE*>::exist && /*确保能够转换*/\ !Conversion</*const*/ BASE*, const void*>::sameType) /*排除BASE为void*的情况*/