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

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

&syw:r$n%r:L0z0  问题:在设计模板函数和模板类时,面对两个型别T和U,我们怎么判断U是否继承自T呢?如果能在编译器判断这样的关系,可以优化泛型编程的函数。在编译器发现这样的关系,就意味着可以不使用dynamic_cast,它会损耗执行效率。51Testing软件测试网n\(a:~3eI:|dc+^

51Testing软件测试网l P/q#}-^ Sxx C

  继承性检测可以转化成一个更一般的问题:检测任意型别T是否可以自动转化成型别U?51Testing软件测试网NH eJHfj

51Testing软件测试网)J!f w j'l#w k

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

b0O"]\\i(F zF0

3Jo;T$`d'|\0  具体方法如下:

{;m/?-eK8[4Y051Testing软件测试网E2t]y%C cxo

  第一步:定义两个不同大小的型别:51Testing软件测试网9N6L-B;[ [

 typedef char Small;51Testing软件测试网O.x(Vb#pU]
  class Big{char dummy[2];};

,J(z+I-l|"u4X0  并且很明显sizeof(Small)<sizeof(Big);

2J/s/I!Z N!H|;d/@051Testing软件测试网3Ob,G DD5z7x

  第二步:定义两个重载函数

&||+n7j H"J0
Small Test(U);
-? B/T8U-Rc~7}z0Big Test(...); //可以接受任何其他对象的函数

l$U enbY0  现在我们可以传递一个T对象给Test()函数,并用sizeof来判断其返回值的大小。51Testing软件测试网Q:}r6v Z/}iK-\

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

"Ab fD|,_ h(|A(A0  T()会调用T对象的默认构造函数,万一T的默认构造函数定义为private,那么T()就会编译失败,所以需要换一种方法来生成对象,用函数的返回值来实现,如下:

9`7EZ_rZ,\0
T MakeT();//不实现
4I`{pW[n!J0const bool convExists=sizeof(Test(MakeT()))==sizeof(Small);
51Testing软件测试网c&GZ[*wjp$NU-Y?/}

  下面用类模板封装起来:51Testing软件测试网Q2|)bFd2ra7E4R

51Testing软件测试网dwvz2?B^!S

template<class T,class U>51Testing软件测试网%d~IPmY'w;G
class Conversion
O}9f?0HfZy0{
Al:m9O l|e0p2I0F0typedef char Small;51Testing软件测试网 DK[y(S+Zts
class Big{char dummy[2]};51Testing软件测试网bI A:~A n
static Small Test(U);
1aq^)ONLd0static Big Test(...);
_ViO4ZtA0static T makeT();51Testing软件测试网R/L%`'_ t!ssd/j]
public:
#e^v5b F"?g0enum{exists=sizeof(Test(MakeT()))==sizeof(Small)};
51Testing软件测试网k/\xTW2y l

,F%v,M5w'UJl!}051Testing软件测试网g7sV\$JD z
};

x;dNHB L:p0

U7k a1O S0  测试代码如下:

L1\'u#wu;L0Conversion<double,int>::exists;
NIJ Qe6zGP0 现在在Conversion中定义两个常数:
51Testing软件测试网;Ue5W/B9G B I g

  a.exists2Way:表示T和U之间是否可以双向转换。51Testing软件测试网Nf!Mo0z@ F

Y5Y.n A n d@0  b.sameType:如果T和U是相同的型别,这个值为true;51Testing软件测试网:Y4P0E glu Y*G

(m.Pi$a|9Z"mxr7\051Testing软件测试网+`0| M:L0v0D:{

template<class T,class U>51Testing软件测试网:wh)v Agcj B
class Conversion51Testing软件测试网?J"K0u:e/No k ^
{51Testing软件测试网1Oh|]/t\$F3VN
typedef char Small;
HDvh"me,B ca0class Big{char dummy[2]};51Testing软件测试网^-v%S}QU] \]
static Small Test(U);
6[ T"OE8U'u\ N0static Big Test(...);
.C#N4m:K.Q^3{0static T makeT();
dr5d0MU*jX)A0public:
n-B[q9t [!@0enum{exists=sizeof(Test(MakeT()))==sizeof(Small)};
Q&a1]0^h0enum{exists2Way=exists&&Conversion<U,T>::exists};51Testing软件测试网D0b|h5OrvSKi
enum{sameType=false};51Testing软件测试网 Q E$qY V \!W
};

3H8o]H2md(y0  下面通过Conversion的偏特化来定义sameType的版本51Testing软件测试网1T.mi(?8Wg{#L

1n)O['G#x;zY'u]6@3r0

,^f~ m j0
template<class T>
/I aef2x C0class Conversion<T,T>
on*P8]AW MD)R0{51Testing软件测试网u s5yn,T G6Z"mZ
public:51Testing软件测试网BL]%~8nz
enum{exists=1,exists2Way=1,sameType=1};51Testing软件测试网z!J4Lha1S"x
};
51Testing软件测试网L-VH~#l6D7T

  判断两个类是否存在继承关系,可以用如下的方式:51Testing软件测试网0|)^'Ku6|y

51Testing软件测试网l-pg,i~9R4~

usu`d?9R,_0
#define SUPERSUBCLASS(T,U) \
d9l%h kBB0(Conversion<const U *,const T *>::exists&& \        //这里函数调用参数次序交换了,表示U是否可以转换为T,即判断T是父类,U是子类。
Rwps5[!EL$h0 !Conversion<const T*,const void*>::sameType)

G b `G A$N0  如果U是public继承自T,或T和U是同一个型别,那么SUPERSUBCLASS(T,U)返回true.51Testing软件测试网g8zW)Y2MRn mb

51Testing软件测试网GM TFeC ^0?+hA

  当SUPERSUBCLASS(T,U)对const U* 和const T *作可转换性检测时,只有三种情况const U *可以隐式的转换为const T*:51Testing软件测试网1\&Q?/gT&yj5XA

6~q^4@'X[o@%`A0  a.T和U是同一个型别

0EP1KP5ccj,o051Testing软件测试网 `G jP$\7`a

  b.T是U的一个public base51Testing软件测试网-JmX2u;F

51Testing软件测试网E6har'a`S${+K*p

  c.T 是void51Testing软件测试网!h$XdFc+F!E

-_*~XzE0  第三种情况可以在前面的!Conversion<const T*,const void*>::sameType处理掉。第一和第二种情况需要用下面更严谨的方法:51Testing软件测试网G0aV-aA nzm

51Testing软件测试网y5o X*vO,s K g{V`

51Testing软件测试网W B0w+l0Q7]z

#define SUPERSUBCLASS_STRICT(T,U) \51Testing软件测试网a)T%N*N&W(s b
(SUPERSUBCLASS(T,U)&& \
aX!bMiM)o$_0!Conversion<const T,const U>::sameType)
51Testing软件测试网KbH)v6ECH

TAG:

 

评分:0

我来说两句

Open Toolbar