crtp是c++中实现静态多态最直接、零运行时开销的方案,其核心是基类模板以派生类为模板参数,通过static_cast在编译期调用派生类成员,要求类型完整、签名精确、继承干净。

CRTP 是目前 C++ 中实现静态多态最直接、零运行时开销的方案,但它不是“继承的语法糖”,而是一套有严格约束的编译期契约。
CRTP 基类模板必须声明为 template
基类没加 template 声明,或者漏掉模板参数,编译器根本不知道 Base 是什么类型。常见错误写法:
-
class Derived : public Base→Base未特化,报错 “use of class template requires template arguments” -
class Derived : public Base<derived></derived>但Base定义里没写template<typename derived></typename>→ 报错 “Baseis not a template”
正确写法只有一种:先定义模板,再继承特化实例。
template <typename derived>
struct ShapeBase {
void draw() { static_cast<derived>(this)->draw_impl(); }
};</derived></typename>
注意:这里 Derived 是占位符,不是具体类型;只有当 Circle 继承 ShapeBase<circle></circle> 时,编译器才生成一份 ShapeBase<circle></circle> 的实参版本。
派生类必须完整定义后才能继承 CRTP 基类
你不能在类声明(前向声明)阶段就让它继承 Base<derived></derived>,否则 static_cast<derived>(this)</derived> 会失败——编译器还不知道 Derived 有哪些成员。
- 错误:
class Circle; class Circle : public ShapeBase<circle> { ... };</circle>→ 编译失败,“invalid static_cast from incomplete type 'Circle'” - 正确:先定义
class Circle { ... };,再写class Circle : public ShapeBase<circle></circle>
这意味着 CRTP 不支持 Pimpl 或循环依赖场景下的提前绑定;如果两个类要互相 CRTP,得用中间策略(比如 traits 或 policy 类)拆解。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
调用派生类成员时,函数签名必须完全匹配
static_cast<derived>(this)->foo()</derived> 不是运行时查找,而是硬编码调用。一旦 foo() 在派生类中不存在、签名不一致(比如 const 修饰不匹配)、或访问权限为 private,错误发生在模板实例化阶段,提示类似:
- “no member named 'draw_impl' in 'Circle'”
- “candidate function not viable: 'this' argument has type 'const Circle', but method is not marked const”
建议在基类接口中显式加 const 限定,并要求派生类实现对应 const 版本,避免隐式转换失败:
template <typename derived>
struct ShapeBase {
void draw() const { static_cast<const derived>(this)->draw_impl(); }
};</const></typename>
多个 CRTP 基类共存时,static_cast 安全边界容易越界
一个类同时继承 Loggable<myclass></myclass> 和 Serializable<myclass></myclass> 是可行的,但要注意:
- 两个基类都用
static_cast<myclass>(this)</myclass>—— 没问题,因为MyClass是最终派生类,指针偏移可算 - 但如果中间插了一层非 CRTP 类(比如
class MyClass : public NonCrtpBase, public Loggable<myclass></myclass>),static_cast<myclass>(this)</myclass>在NonCrtpBase子对象里可能指向错误地址 - 更隐蔽的问题:若某 CRTP 基类内部又继承了另一个模板基类(比如
Base<derived> : public Policy<derived></derived></derived>),而Policy也做同类static_cast,容易触发 ODR 违规或 SFINAE 失败
这种嵌套 CRTP 场景下,最好把所有 static_cast 收口到最顶层基类,中间层改用传入引用或指针参数,避免多次转型叠加。
CRTP 的“静态”二字,本质是把多态决策压到模板实例化那一刻;它不难写,但真正难的是守住那个编译期契约:类型完整、签名精确、继承干净。一旦破戒,报错信息往往指向模板深处,而不是你写的那行 static_cast。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










