CRTP是编译期强制的模板继承约定:派生类必须显式以自身为模板参数继承基类(如class Derived : public Base),否则直接编译失败;其核心是静态绑定、接口约束与类型安全,而非运行时多态。

CRTP 不是“另一种多态写法”,它是编译期绑定的强制约定:只要派生类没按模板参数形式继承,代码就根本编译不过。
CRTP 的模板参数必须显式传入 Derived,不能省略或推导
常见错误是写成 class Derived : public Base —— 这里 Base 是个模板名,不是具体类型,编译器直接报错 “use of class template ‘Base’ requires template arguments”。正确写法只有一种:class Derived : public Base<derived></derived>。注意不是 Base<derived></derived>(大小写敏感),也不是 Base<decltype></decltype>(this 在继承声明时还不存在)。这个模板实参必须是最终派生类的**完整定义类型**,所以不能在类声明(仅前置声明)阶段就继承 Base<derived></derived>,否则触发 ODR 或不完整类型错误。
static_cast 调用派生类成员前,必须确保该成员已声明且可访问
基类中写 static_cast<derived>(this)->foo()</derived> 时,编译器会在实例化 Base<derived></derived> 的那一刻检查 Derived 是否有 foo 成员。如果漏定义、拼错名、或 foo 是私有成员且未加友元,错误发生在模板实例化阶段,提示类似 “no member named ‘foo’ in ‘Derived’”。这不是运行时崩溃,而是硬性编译失败。建议在基类构造函数里加 static_assert 做兜底:static_assert(std::is_member_function_pointer_v<decltype>, "Derived must implement foo")</decltype>。
CRTP 不提供 is_base_of 关系,泛型约束要换判断方式
因为 Base<derived></derived> 和 Derived 是两个独立的模板特化,std::is_base_of_v<base>, Derived> 永远为 false。若你写算法要求“接受所有 CRTP 派生类”,不能靠继承关系判断,得改用 std::is_convertible_v<derived base>*> </derived> 或更稳妥的 SFINAE 检测:requires { std::declval<derived>().interface(); }</derived>。这点在写通用容器适配器或策略组合时极易踩坑——你以为能用 enable_if 过滤,结果模板推导直接失败。
立即学习“C++免费学习笔记(深入)”;
多重 CRTP 继承时,static_cast 的目标类型必须精确匹配当前基类的模板参数
比如同时继承 Loggable<derived></derived> 和 Serializable<derived></derived>,两个基类内部都用 static_cast<derived>(this)</derived> 是安全的。但如果中间插了一层非 CRTP 类:class Middle : public Base<derived></derived>,再让 Final : public Middle, Loggable<final></final>,那 Loggable<final></final> 里的 static_cast<final>(this)</final> 就会越界——this 实际指向的是 Middle 子对象起始地址,不是 Final 的完整对象头。这种布局错位会导致未定义行为,且编译器通常不报错。解决方法只有两个:避免混合 CRTP 与普通继承;或统一所有基类都用同一份 Derived 模板参数,并确保最终派生类直接继承它们。
CRTP 的真正门槛不在语法,而在它把类型契约从运行时检查提前到了模板实例化边界——一旦跨过这个边界,错误就不再是 segfault,而是整个编译单元拒之门外。


















