如何在编译时刻判断两个类型是否可以自动转换?——《Modern C++ Design》读书笔记(1)
Posted on 2005-01-24 23:56 andrew 阅读(1541) 评论(0) 编辑 收藏 举报如何在编译时刻判断两个类型是否可以自动转换?
——《Modern C++ Design》读书笔记(1)
设T和U是任意的两个类型,用什么方法判定类型T可以自动转换到类型U呢?
答案是:借助sizeof运算符。
其实,sizeof的实力相当强大。不论是什么类型,也不论是多么复杂的
表达式,只要把它交给sizeof,结果都将返回该表达式的值的类型长度。
也就是说,sizeof的背后必须暗藏一整套推导机制,它可以推导出表达式
值的类型。最终,sizeof并不关心整个表达式,而只是返回结果的长度。
我们的基本思想是,使用sizeof和函数重载机制。
声明两个重载函数,一个函数的形参接受可以转换为U的类型;另一个函数的
形参接受任何其他类型。使用类型T的临时变量来调用重载函数。如果接受U的函数
被调用,则可断定T是能够转换为U的。如果接受任何其他类型作参数的函数被调用
,那么T不能转换为U。
为了判断到底哪一个函数被调用,我们要让两个函数的返回类型具有不同的大小,
然后用sizeof来检查它们。只要能区分出来大小,类型本身是什么倒无关紧要。
首先,建立两个大小不同的类型:
typedef char Small;
class Big { char dummy[2]; };
根据C++标准规定sizeof(char)等于1,于是sizeof(Small)等于1。Big的大小我们
不知道,但是它肯定比1要大。
然后,声明一个函数,它接受U作为参数类型,并返回Small:
Small Test(U);
再声明一个重载函数版本,它必须在不能进行自动类型转换的情况下才被系统调用,
这就需要使用到省略号参数了:
Big Test(...);
下面对Test函数调用应用sizeof运算符:
bool const convExists = sizeof(Test(T())) == sizeof(Small);
对了,就是它!这里T()表示调用T的缺省构造函数建立一个临时变量,sizeof会从
这个表达式中抽取出它的值的长度。长度只能是两个值:sizeof(Small)或sizeof(Big)
中的一个。这取决于那个版本的重载函数会被调用,而事实是只有在T可以自动转换
为U的情况下Small Test(U)函数才会调用,从而convExists==true。
有一个问题:如果T不存在缺省构造函数,那么上述表达式在求值过程中岂不会出现
编译错误?该如何解决呢?可以巧妙的使用如下函数:
T MakeT(); // 没有实现定义
bool const convExists = sizeof(Test(MakeT())) == sizeof(Small);
为什么这种方案可行呢?关键在于sizeof的神奇特性:因为sizeof在推导类型长度的
过程中,并不实际求取任何表达式的值,没有表达式会被真正地计算出来。
好了,一切就绪。现在我们可以将上述方法用一个类模版封装起来,它将封装所有
的类型推导,而仅提供结果数据:
template<class T, class U>
class Conversion
{
type char Small;
class Big { char dummy[2]; };
static Small Test(U);
static Big Test(...);
static T MakeT();
public:
enum { exists =
sizeof(Test(MakeT())) == sizeof(Small) };
};
下面编写主函数来测试Conversion类:
int main()
{
using namespace std;
cout << Conversion<double, int>::exists << ' '
<< Conversion<char, char*>::exists << ' '
<< Conversion<size_t, vector<int> >::exists << endl;
}
该程序输出结果:
1 0 0
说明:虽然std::vector存在一个接受size_t作为参数的构造函数,但是由于
该构造函数被声明为了explicit,因此不能从size_t自动转换为std::vector.
另外,我们还可以在Conversion中实现一个常量sameType,以判断T和U是否
具有相同的类型:
template<class T, class U> // 主模板
class Conversion
{
type char Small;
class Big { char dummy[2]; };
static Small Test(U);
static Big Test(...);
static T MakeT();
public:
enum { exists =
sizeof(Test(MakeT())) == sizeof(Small) };
enum { sameType = false };
};
template<class T> // 部分特化模板
class Conversion<T, T>
{
public:
enum { exists = true, sameType = true };
}
当T和U的类型一样时,部分特化模板会被优先实例化,因此exists和sameType都为true。
借助于Conversion模板类,我们很容易判断T和U是否存在继承关系,具体如何进行,还是
等到下次再说吧……