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

本文介绍了一种在编译时判断C++中两种类型继承关系的方法,利用sizeof和重载函数特性,实现类型转换判断,并封装为通用模板。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

一段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个不同的类型:

None.gif typedef  char  Small;
ExpandedBlockStart.gifContractedBlock.gif
class  Big  dot.gif char  dummy[ 2 ]; } ;
None.gif
None.gif

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

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

Small Test(U);

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

Big Test(...);

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

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

None.gif const   bool  convExist  =   sizeof (Test(T()))  ==   sizeof (Small);
None.gif
None.gif

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

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

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

None.gif template  < class  T,  class  U >
ExpandedBlockStart.gifContractedBlock.gif
class  Conversion  dot.gif {
InBlock.gif    typedef 
char  Small;
ExpandedSubBlockStart.gifContractedSubBlock.gif    
class  Big  dot.gif char  dummy[ 2 ]; } ;
InBlock.gif
InBlock.gif    
static  Small Test(U);
InBlock.gif    
static  Big Test(dot.gif);
InBlock.gif    
static  T MakeT();
InBlock.gif
InBlock.gif
public :
ExpandedSubBlockStart.gifContractedSubBlock.gif    
enum   dot.gif { exist  =   sizeof (Test(MakeT()))  ==   sizeof (Small) } ;
ExpandedBlockEnd.gif}
;
None.gif
None.gif

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

None.gif template  < class  T,  class  U >
ExpandedBlockStart.gifContractedBlock.gif
class  Conversion  dot.gif {
InBlock.gif    .. 
as  above..
ExpandedSubBlockStart.gifContractedSubBlock.gif    
enum   dot.gif { sameType  =   false }
ExpandedBlockEnd.gif}
;
None.gif
None.gif

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

None.gif template  < class  T >
ExpandedBlockStart.gifContractedBlock.gif
class  Conversion < T, T >   dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif    
enum   dot.gif { exists  =   1 , sameType  =   1  } ;
ExpandedBlockEnd.gif}
;

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

None.gif #define  SUPERSUBCLASS(T, U) \
None.gif    (Conversion
< const  U * const  T * )::exists  &&  \
None.gif    
! 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 个测试屏蔽了最后一种情形。当然,如果你认为同一种类型也不算是继承关系的话,可以进一步严格其条件:

None.gif #define  SUPERSUBCLASS_STRICT(T, U) \
None.gif    (SUPERSUBCLASS(T, U) 
&&  \
None.gif    
! Conversion( const  T,  const  U)::sameType)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值