编译期间侦测可转换性和继承性

如何发现类型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*的情况*/

posted on 2011-03-18 14:06  Observer  阅读(325)  评论(0编辑  收藏  举报

导航