在编译时决定类型的可继承性 .

在编译时决定类型的可继承性

Posted on 2006-05-15 17:20 nacci 阅读(478) 评论(0)  编辑 收藏 引用 所属分类: C++漫谈

一段C++的读书笔记

给定任意2个类型UT,你如何来确定U是否继承于T呢?在编译时发现两个类型的这种关系对于泛型库的优化是极为重要的。在泛型函数中,如果某个类实现了特定的接口,你可以根据这种关系为其利用特定的优化算法。另外,如果我们可以在编译期决定2个类的关系,我们也可以远离dynamic_cast,从而避免运行时的效率开销。



在着手解决这个问题之前,我们先来考虑一个更为一般的问题。假设我们有2个任意类型UT,如何确定T能否自动转换成U呢?

答案也许让你有些惊讶,我们可以利用sizeof来帮忙。你可以把sizeof用在任何复杂的表达式上, sizeof可以返回这个表达式值的大小,而不会在运行时评估表达式的值。这也就意味着,你可以把函数重载、模版实例化、转换规则等等所有你可以在C++表达式中使用的设施统统塞到sizeof中来。实际上,sizeof隐藏了一个可以演绎表达式类型的设施,最终,sizeof会返回表达式结果的类型。

这样我们就可以通过sizeof和重载函数来解决判断类型之间的可转换性的问题。思路很简单:我们提供2个重载函数,一个函数的参数是我们要转换成的类型(我们用U表示),而另一个则用来接收其他所有类型的参数。然后我们把要检测的类型(用T表示)传递给重载函数。如果接受类型U为参数的函数被调用了,我们就认为T可以转换为U,反之则不可以。如何确定哪个函数被调用了呢?我们利用sizeof出马,我们只要让重载函数返回不同的类型,然后检查一下返回值就可以了。

实践一下:

首先,定义2个不同的类型:

typedef  char  Small;
class  Big  char  dummy[ 2 ]; } ;

默认情况下, sizeof(Small) 1 ,而 Big 的大小则无关紧要,我们只要知道肯定不是 1 就好了。

其次,定义 2 个重载函数,一个接收要转换成的类型:

Small Test(U);

另一个用来接收“其他的所有类型”,我们要保证在排除所有的转换之后才调用这个函数, OK, 用省略号表示的参数列表真好满足需求

Big Test(...);

尽管把一个 C++ 对象传递给 ... 参数类型的函数,其结果未定义,但是实际上我们并没有调用这个函数。我们甚至可以不用实现它。

最后,我们用 sizeof 判断一下就完成任务了:

const   bool  convExist  =   sizeof (Test(T()))  ==   sizeof (Small);

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

T MakeT();
const   bool  convExist  =   sizeof (Test(MakeT()))  ==   sizeof (Small);

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

template  < class  T,  class  U >
class  Conversion  {
    typedef 
char  Small;
    
class  Big  char  dummy[ 2 ]; } ;

    
static  Small Test(U);
    
static  Big Test();
    
static  T MakeT();

public :
    
enum   { exist  =   sizeof (Test(MakeT()))  ==   sizeof (Small) } ;
}
;

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

template  < class  T,  class  U >
class  Conversion  {
    .. 
as  above..
    
enum   { sameType  =   false }
}
;

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

template  < class  T >
class  Conversion < T, T >   {
    
enum   { exists  =   1 , sameType  =   1  } ;
}
;

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

#define  SUPERSUBCLASS(T, U) \
    (Conversion
< const  U * const  T * )::exists  &&  \
    
! Conversion < const  T * const   void *> ::sameType)

U 继承于 T 或者 U T 是同一个类型的时候, SUPERSUBCLASS 返回 true 。总结一下,只有下面这 3 种情形 const U* 可以隐式转换到 const T*

1.      T U 是同一个类型

2.      T U 的任意一个基类

3.      T void

我们通过第 2 个测试屏蔽了最后一种情形。当然,如果你认为同一种类型也不算是继承关系的话,可以进一步严格其条件:

#define  SUPERSUBCLASS_STRICT(T, U) \
    (SUPERSUBCLASS(T, U) 
&&  \
    
! Conversion( const  T,  const  U)::sameType)

Powered by Jekyll and Theme by solid

本站总访问量