CRTP是C++中实现零虚函数、零对象尺寸开销、零间接调用成本的编译期多态唯一方案;其要求基类为模板且派生类必须显式继承Base,否则因类型不完整或继承关系不成立而编译失败。

CRTP 是目前 C++ 中唯一能同时做到零虚函数、零对象尺寸开销、零间接调用成本的编译期多态方案,但它不是“写了模板就能跑”,所有错误都在实例化期爆发,且报错位置常反直觉。
为什么 class Derived : public Base 一定编译失败
CRTP 的基类 Base 必须是模板,且派生类必须显式传入自身完整类型作为模板实参。漏掉模板参数,Base 就是个不完整类型,连声明都算不上。
-
class Derived : public Base→ 编译器报error: use of class template 'Base' requires template arguments -
class Derived : public Base→Base和Derived无继承关系,static_cast直接失败(this) - 正确写法只有一种:
class Derived : public Base,且Derived必须是完整定义(不能是前置声明)
static_cast(this) 是唯一合法转型方式
CRTP 的安全性完全依赖这个转换:它要求编译器在模板实例化时就确认 Base 和 Derived 之间存在公有、标准布局的继承关系。其他方式要么编译不过,要么运行时崩溃。
-
dynamic_cast要求基类有虚函数,直接违反前提,报error: cannot dynamic_cast ... because base class has no virtual functions -
reinterpret_cast绕过类型系统,若Derived含多重继承或空基类优化,this地址偏移与Base子对象不一致,访问成员即 UB -
static_cast在 Clang/GCC 开-Wall下会校验继承路径;若误用于非派生类,通常在构造函数里就报invalid static_cast
派生类函数签名必须和基类调用点完全匹配
CRTP 没有虚函数表兜底,也没有运行时名称查找。基类中 static_cast 这一行,会强制要求 Derived 精确提供 foo() 成员函数——包括 const 限定、参数类型、返回值、甚至默认参数是否声明。
- 基类调
foo(int),派生类只定义foo(double)→ 报no matching member function - 基类调非 const 版本,派生类只提供
void foo() const→ 编译失败,const 不匹配 - 想让不同派生类提供不同接口?得用
if constexpr+ trait 判断,例如:if constexpr (has_member_v) { ... }
CRTP 类型天然不兼容,别指望统一容器
Base 和 Base 是两个毫无关系的类型,C++ 没有 Base>* 这种通配语法。试图把它们塞进同一 std::vector 或用同一指针类型持有,只会触发编译错误或 ODR 违规。
-
std::vector<:unique_ptr>>行不通 ——Shape不是公共基类,Base和Base无继承关系 - 可行替代:
std::variant+std::visit,或外层加一层虚接口桥接(如让Circle同时继承Base和Drawable) - 代码膨胀问题真实存在:
Base和Base的每个成员函数都会独立实例化,哪怕逻辑一模一样
最易被忽略的一点:CRTP 的所有约束都发生在**模板实例化时刻**,而非链接或运行时。这意味着头文件顺序、定义可见性、以及派生类是否完整,全由你手动控制——编译器不会帮你延迟解析,也不会给你“稍后补定义”的机会。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











