【C++泛型编程】编译期可转换性和继承性检测

上一篇 / 下一篇  2012-09-05 09:54:11 / 个人分类:C++

caG%lvz0  问题:在设计模板函数和模板类时,面对两个型别T和U,我们怎么判断U是否继承自T呢?如果能在编译器判断这样的关系,可以优化泛型编程的函数。在编译器发现这样的关系,就意味着可以不使用dynamic_cast,它会损耗执行效率。

wHW;K3\'i!m/q_0

o;d1|y2P u;W8qBU-B9U0  继承性检测可以转化成一个更一般的问题:检测任意型别T是否可以自动转化成型别U?51Testing软件测试网 ^U i9F\5paw

]q,yo%Yv#R;\k0  解决方法:可 以使用运用sizeof和函数重载的方法来检测这种可转换性。编写两个重载函数:一个接受U(U代表转换目标)的函数,另一个接受“任何型别”的函数。用 型别为T的暂时对象来调用这些重载函数,而“T是否可以转化为U”是我们想知道的。如果接受U的那个函数被调用,就可以判断T可以转换为U;否则T无法转 换为U。为了知道哪个函数被调用,这两个重载函数以不同大小的型别返回,用sizeof来区分其大小。

t_!q"A$e051Testing软件测试网 y y,w4ly(^

  具体方法如下:

p;c[z Z051Testing软件测试网2@&_QxbsHJ

  第一步:定义两个不同大小的型别:

&\1aAX7F0Z]3a0
 typedef char Small;51Testing软件测试网3EgLm9C
  class Big{char dummy[2];};
51Testing软件测试网6y'T7|8\'^,e5y

  并且很明显sizeof(Small)<sizeof(Big);

!p)IO`Sa8@v)G0

_ ld-kf%f4y!dp0  第二步:定义两个重载函数

0eMBE9u0g)]0
Small Test(U);
[3X&lW*lq/VT1Q?/H0Big Test(...); //可以接受任何其他对象的函数

xRsO;Q Z0  现在我们可以传递一个T对象给Test()函数,并用sizeof来判断其返回值的大小。

u ?N:|`y8ca)~,S&v0
const bool convExists=sizeof(Test(T()))==sizeof(Small);

-vJY%qfl,M%d M-O5o0  T()会调用T对象的默认构造函数,万一T的默认构造函数定义为private,那么T()就会编译失败,所以需要换一种方法来生成对象,用函数的返回值来实现,如下:51Testing软件测试网&Ql%Wa.@`"]r6^

T MakeT();//不实现51Testing软件测试网!Un8s]:~7@ HE T3`[
const bool convExists=sizeof(Test(MakeT()))==sizeof(Small);

@ynl?gS~/L0  下面用类模板封装起来:

)a;KIB JQ"J2Cfg0

O _%d6V8X%aS HH)|0template<class T,class U>51Testing软件测试网'S1Ob GJZ~1M
class Conversion
U,b4vT Si!FDKm0{
3H3Vl'zQW0typedef char Small;51Testing软件测试网6d)i4m2n/e]:A"o c4?N
class Big{char dummy[2]};
B#tP9X;F%{l'[0static Small Test(U);
n yqj|.yx0static Big Test(...);
;yXZfZ6BmGg0static T makeT();51Testing软件测试网/OS bO;_l} u
public:51Testing软件测试网GVB D,|K ki
enum{exists=sizeof(Test(MakeT()))==sizeof(Small)};

,y5I,k+c2h/?g0

z3_i,Y ~$o3EL @0
2S/k6nc]tir~0};

6IF{#Wr0
51Testing软件测试网 ]H NWZ$m

  测试代码如下:51Testing软件测试网7\0`6hK/T"S

Conversion<double,int>::exists;51Testing软件测试网6XPMnr'y
 现在在Conversion中定义两个常数:
51Testing软件测试网0b.};u2U"P D9ao7RJtJ

  a.exists2Way:表示T和U之间是否可以双向转换。

*c\}.ol_ }051Testing软件测试网5P`"Ns`uq!P{+}{

  b.sameType:如果T和U是相同的型别,这个值为true;51Testing软件测试网!s\ ~ s)K%O

51Testing软件测试网bY%K{6rf

%PU Gb"G*b}Qb0
template<class T,class U>
,k%i%WV6c p_g}E0class Conversion51Testing软件测试网;D|'^T-k;[[e3[&z
{
Sly~kl1[ln0typedef char Small;
vS*ji({0class Big{char dummy[2]};
"|q&m"h2G;kK~-C0static Small Test(U);51Testing软件测试网 OmyuyOTU
static Big Test(...);51Testing软件测试网 a,@ gk-aVs
static T makeT();
MxIS rh0Nu0public:
S J2znG9FRB%| S2cP0enum{exists=sizeof(Test(MakeT()))==sizeof(Small)};51Testing软件测试网"X)mb:_N/D
enum{exists2Way=exists&&Conversion<U,T>::exists};51Testing软件测试网*oY~ M*w9lMS
enum{sameType=false};
D1WYzQ5@:Xj6Dt7o0};

y1Xs9` R!ss+i0  下面通过Conversion的偏特化来定义sameType的版本

[3` Mw%G0

fP,b8M,@S&].q0

v%u S ~8C&R+|H:fg0
template<class T>51Testing软件测试网S z Z"_%|8|C
class Conversion<T,T>
On4l%\8` Z!jLaBh0{
]5OO1n2\scn0public:
5^9J6|i-h4T^(z] B0enum{exists=1,exists2Way=1,sameType=1};51Testing软件测试网 i$v2e*VT0NAZ qHM4z
};
51Testing软件测试网H+hwB H"Y

  判断两个类是否存在继承关系,可以用如下的方式:51Testing软件测试网!ey&uZ$X)]3B.O

51Testing软件测试网,qg%D;Q'r MS

2m q2H/pA!fNr.F%n0
#define SUPERSUBCLASS(T,U) \
.T2e/yZ5{#y#q0(Conversion<const U *,const T *>::exists&& \        //这里函数调用参数次序交换了,表示U是否可以转换为T,即判断T是父类,U是子类。
-qu5E&v7cK]V0 !Conversion<const T*,const void*>::sameType)

N*n tl9kB0  如果U是public继承自T,或T和U是同一个型别,那么SUPERSUBCLASS(T,U)返回true.51Testing软件测试网1ue%}NJGM

51Testing软件测试网8D kR:tP

  当SUPERSUBCLASS(T,U)对const U* 和const T *作可转换性检测时,只有三种情况const U *可以隐式的转换为const T*:

/qs[8{K6wYY+_)B0

w*KWI i"k6e0  a.T和U是同一个型别51Testing软件测试网8TmXT(z/Q

51Testing软件测试网 f3| O SgV

  b.T是U的一个public base51Testing软件测试网-WW Eg(mS)ms~

51Testing软件测试网L,l `A-S

  c.T 是void

e+f+Eb9r051Testing软件测试网1x ?6Kr2g8{&E}G

  第三种情况可以在前面的!Conversion<const T*,const void*>::sameType处理掉。第一和第二种情况需要用下面更严谨的方法:

pvyW_I0

)jiRzHHZ0

!KdJ?\9y0
#define SUPERSUBCLASS_STRICT(T,U) \51Testing软件测试网3^!C'@6m)KF)M)yU%g
(SUPERSUBCLASS(T,U)&& \
k[tHg!iN8}:h;d y0!Conversion<const T,const U>::sameType)
51Testing软件测试网x| f#R*}wCn

TAG:

 

评分:0

我来说两句

Open Toolbar