请解释这段可正常编译的C++类继承关系检测代码
拆解这段编译期基类检测代码
嘿,这段代码是个经典的编译期类型检查技巧,用来判断一个类D是不是继承自类B(包括直接继承、间接继承,甚至D就是B本身的情况),而且完全没有运行时开销——所有判断都在编译阶段完成。咱们一步步拆解它的工作原理:
核心辅助类:IsDerivedFromHelper
这个模板类是整个逻辑的核心,里面的每一部分都有特定作用:
- 首先定义了两个空辅助类:
No和Yes。Yes里嵌套了一个No no[3]的数组,这意味着sizeof(Yes)必然大于sizeof(No)——这是后续判断的关键区分标志。 - 然后是两个重载的静态
Test函数:static Yes Test( B* );:接受一个B*类型的参数,返回Yes类型static No Test( ... );:变参版本,接受任意参数,返回No类型
- 最后是一个枚举常量
Is:enum { Is = sizeof(Test(static_cast<D*>(0))) == sizeof(Yes) };
关键逻辑:函数重载匹配 + sizeof编译期计算
这里的魔法在于C++的函数重载解析规则,以及sizeof是编译期运算符的特性:
- 当我们调用
Test(static_cast<D*>(0))时:- 如果
D是B的public派生类(或者D就是B),那么D*可以隐式转换为B*,编译器会优先匹配第一个Test(B*)版本,此时sizeof(Test(...))就是sizeof(Yes)。 - 如果
D和B没有继承关系,或者是private/protected继承(此时派生类指针无法隐式转换为基类指针),编译器会匹配到变参的Test(...)版本,此时sizeof(Test(...))就是sizeof(No)。
- 如果
- 通过比较这两个
sizeof的结果,就能在编译期确定Is的值是true还是false。
对外接口:IsDerivedFrom函数模板
这个外层的函数模板只是把IsDerivedFromHelper里的编译期常量Is包装成一个更易用的函数接口,调用的时候只需要传入两个类型参数即可,比如:
class Base {}; class Derived : public Base {}; class Unrelated {}; // 这些结果在编译时就已经确定,完全没有运行时开销 bool derived_from_base = IsDerivedFrom<Derived, Base>(); // 返回true bool base_is_self = IsDerivedFrom<Base, Base>(); // 返回true bool unrelated_not_derived = IsDerivedFrom<Unrelated, Base>(); // 返回false
补充细节
- 为什么用
static_cast<D*>(0)而不是nullptr?这段代码应该是C++11之前的实现,当时还没有nullptr关键字,用0强制转换为指针类型是标准写法。 - 关于private/protected继承:这种情况下检测会返回
false,这其实是合理的——因为private/protected继承的意图就是不让外部代码认为派生类是基类的"公开子类",所以编译期检测不到也是符合设计初衷的。
内容的提问来源于stack exchange,提问作者jpb123
相关产品推荐
相关产品推荐

