一段C++的读书笔记
给定任意2个类型U和T,你如何来确定U是否继承于T呢?在编译时发现两个类型的这种关系对于泛型库的优化是极为重要的。在泛型函数中,如果某个类实现了特定的接口,你可以根据这种关系为其利用特定的优化算法。另外,如果我们可以在编译期决定2个类的关系,我们也可以远离dynamic_cast,从而避免运行时的效率开销。
在着手解决这个问题之前,我们先来考虑一个更为一般的问题。假设我们有2个任意类型U和T,如何确定T能否自动转换成U呢?
答案也许让你有些惊讶,我们可以利用sizeof来帮忙。你可以把sizeof用在任何复杂的表达式上, sizeof可以返回这个表达式值的大小,而不会在运行时评估表达式的值。这也就意味着,你可以把函数重载、模版实例化、转换规则等等所有你可以在C++表达式中使用的设施统统塞到sizeof中来。实际上,sizeof隐藏了一个可以演绎表达式类型的设施,最终,sizeof会返回表达式结果的类型。
这样我们就可以通过sizeof和重载函数来解决判断类型之间的可转换性的问题。思路很简单:我们提供2个重载函数,一个函数的参数是我们要转换成的类型(我们用U表示),而另一个则用来接收其他所有类型的参数。然后我们把要检测的类型(用T表示)传递给重载函数。如果接受类型U为参数的函数被调用了,我们就认为T可以转换为U,反之则不可以。如何确定哪个函数被调用了呢?我们利用sizeof出马,我们只要让重载函数返回不同的类型,然后检查一下返回值就可以了。
实践一下:
首先,定义2个不同的类型:






默认情况下, sizeof(Small) 是 1 ,而 Big 的大小则无关紧要,我们只要知道肯定不是 1 就好了。
其次,定义 2 个重载函数,一个接收要转换成的类型:
Small Test(U);
另一个用来接收“其他的所有类型”,我们要保证在排除所有的转换之后才调用这个函数, OK, 用省略号表示的参数列表真好满足需求
Big Test(...);
尽管把一个 C++ 对象传递给 ... 参数类型的函数,其结果未定义,但是实际上我们并没有调用这个函数。我们甚至可以不用实现它。
最后,我们用 sizeof 判断一下就完成任务了:



你也许会说,就是它了! Test 的调用会创建一个临时对象 T ,之后可能的结果只能是 sizeof(Small) 或 sizeof(Big) 。兴奋之余,我们还要看到一个问题。如果 T 的构造函数被设计成 private ,我们就前功尽弃了。当然解决的方法也很简单,定义一个函数,让他返回类型为 T 的对象。




最后,把刚才的东西封装到一个类里:





















另外,我们还可以设置另外一个常量 Conversion::SameType ,如果 T 和 U 表示同一个类型,那么返回 true :











之后为同一个类型设计一个偏特化版本:








最后,回到我们的主题,通过 Conversion 的帮助,我们可以来决定两个类型的继承性了。



当 U 继承于 T 或者 U 和 T 是同一个类型的时候, SUPERSUBCLASS 返回 true 。总结一下,只有下面这 3 种情形 const U* 可以隐式转换到 const T* :
1. T 和 U 是同一个类型
2. T 是 U 的任意一个基类
3. T 是 void
我们通过第 2 个测试屏蔽了最后一种情形。当然,如果你认为同一种类型也不算是继承关系的话,可以进一步严格其条件:


