编译期检查class是否有继承关系

最近看sigc++的代码,看到其中有编译期检测class是否有继承关系的代码,觉得比较精妙,于是拿出来剖析一下

 

代码中的注释和很多不必要的#ifdef已经被我去掉,该段代码的用法如下,假设有

 

struct foo{};

struct bar{};

 

如果foo不是直接或者间接继承自bar,那么is_base_and_drived<bar,foo>::value = false

如果

struct bar{};

struct foo:public bar

{};

 

那么这种情况下 is_base_and_drived<bar,foo>::value = true

 

那么这段代码是怎么做到的呢

 

 

 

 

 

观看上面的代码,我们大概可以看到,手法是通过 is_base_class这个重载函数来实现的,通过is_base_class的不同参数,返回不同的类型,而且让返回类型的大小不一样,加以区分。

 

static big is_base_class(...),接受任意类型的参数,返回struct big,而sizeof(struct big)的大小一般情况下,在这里定义的,是64

 

static char is_base_class(typename type_trait<T_base>::pointer),接受的则是T_base类型的指针,返回一个char,sizeof(char)一般情况下应该是1,总之能够跟上面的64明显的区分出来

 

这里解释一下type_trait是干嘛的,很多时候,我们传的T_base可能是一个class,或者一个,class引用类型,或者一个指针类型

 

比如struct foo{},然后这里的T_base可能是foo,也可能是foo&,或者是foo*等等,因此type_trait在这里的作用就是把T_base真正的类型萃取出来,比如type_trait<foo>::pointer就是foo*,而type_trait<foo&>::pointer的类型也应该是foo*

 

因此编译器在某处调用is_base_class的地方,会根据参数来决定调用哪个重载,如果是T_base*的话,会调用返回char的那个重载,否则就调用返回big的那个重载了。

 

这里还有问题,因为下面调用is_base_class的地方,

 

static const bool value =
    sizeof(internal_class::is_base_class_(reinterpret_cast<typename type_trait<T_derived>::pointer>(0))) ==
    sizeof(char);

 

这里可没有出现T_base*之类的呀,唯一的转换是吧NULL指针,或者0指针,转换成T_drived*啊,这个能起作用吗?能调用到正确的函数吗?

 

到这里好好想想,C++的继承体系中,基类的指针可以指向派生类,因此把一个派生类的指针转换成一个基类的指针,是安全的,反之,把一个派生类的指针,转换成基类指针则是不安全的。

 

想到这里,我们明白过来了,在这里,用一个派生类的空的指针,当然,万能的编译器是知道T_drived和T_base是不是有派生关系的,于是在转化成T_drived指针之后,如果T_drived跟T_base是有继承关系的,那么编译器可以顺利的把T_drived*提升成T_base*,于是找到了返回char的最合适的is_base_class来调用,反之,如果没有继承关系,不能提升,那就只有调用返回big的那个重载了

 

于是这里value的值也就可以决定了。

 

template <class T_base>
struct is_base_and_derived<T_base, T_base>
{
  static const bool value = true;
};

 

这个则是为了语义完备性,因为是is_base_and_drived,因此当base和base作比较的时候,value也应该是true的

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值