转载至:http://pthread.blog.163.com/blog/static/1693081782010715104330491/
最近看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
那么这段代码是怎么做到的呢
template <class T_base, class T_derived>
struct is_base_and_derived
{
private:
struct big {
char memory[64];
};
static big is_base_class_(...);
static char is_base_class_(typename type_trait<T_base>::pointer);
public:
static const bool value =
sizeof(is_base_class_(reinterpret_cast<typename type_trait<T_derived>::pointer>(0))) ==
sizeof(char);
};
template <class T_base>
struct is_base_and_derived<T_base, T_base>
{
static const bool 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(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的