编译期间侦测可转换性和继承性
如何发现类型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*的情况*/
浙公网安备 33010602011771号