crtp是编译期确定调用路径的静态多态方案,要求派生类显式传入自身类型、严格匹配函数签名,不支持运行时类型统一存储,错误需手动校验。

不用虚函数也能实现多态,而且调用路径在编译期就完全确定——CRTP 是目前最可靠、最广泛用于生产环境的静态多态方案,但它不是“写对模板参数就完事”,稍有疏忽就会触发 static_cast 崩溃或模板实例化失败。
CRTP 基类必须显式依赖派生类类型
CRTP 的核心是让基类知道“谁在继承我”。这不能靠运行时推导,必须由派生类在继承时主动传入自身类型:
template <typename derived>
class ShapeBase {
public:
void draw() {
static_cast<derived>(this)->draw_impl(); // 编译期绑定
}
};
<p>class Circle : public ShapeBase<circle> { // ← 必须写 Circle,不能写 auto 或 void
public:
void draw_impl() { /<em> ... </em>/ }
};
</circle></p></derived></typename>
常见错误包括:
- 漏写模板参数:
class Circle : public ShapeBase→ 编译失败,ShapeBase未实例化 - 传错类型:
class Circle : public ShapeBase<square></square>→static_cast在运行时可能转成错误类型(虽多数编译器会报错,但非强制) - 派生类没实现
draw_impl()→ 编译错误,提示member function not found,这是优势,也是约束
派生类成员函数名必须严格匹配基类调用点
CRTP 不做名称查找优化,static_cast<derived>(this)->draw_impl()</derived> 要求 Derived 确实存在名为 draw_impl 的可访问成员函数。它不接受重载、别名或 const/volatile 修饰不一致的情况:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
-
void draw_impl() const和void draw_impl()视为不同函数 → 基类调用非 const 版本时,派生类只提供 const 版本会编译失败 - 函数签名中参数个数、类型、默认值都必须完全一致;返回类型也要协变兼容(通常建议保持相同)
- 若想支持多种接口风格,得靠 SFINAE 或
requires(C++20)约束,不能靠“自动适配”
CRTP 与模板参数推导不兼容,无法用于泛型容器存储
你不能把 ShapeBase<circle></circle> 和 ShapeBase<square></square> 放进同一个 std::vector,因为它们是完全不同的类型:
-
std::vector<shapebase>></shapebase>语法非法 —— C++ 没有“模板类型擦除”语法糖 - 想统一持有,只能退回到动态多态(加虚函数),或用
std::variant<circle square></circle>+ 访问者模式,或用type-erased包装器(如std::any或自定义小对象封装) - CRTP 的零开销是以牺牲运行时类型灵活性为代价的;它适合编译期已知所有子类的场景(如数学库中的
Vector<float></float>、Vector<double></double>)
CRTP 类型嵌套过深时,编译错误信息极难读
当 CRTP 套了两层以上(比如策略模板再继承 CRTP 基类),或者结合 constexpr if 和 requires,编译器报错常卡在 static_cast 或模板实例化栈深处,错误位置和真正问题点可能隔了七八行模板展开:
- 优先在基类里加
static_assert检查Derived是否满足接口,例如:static_assert(std::is_member_function_pointer_v<decltype>)</decltype> - 避免在 CRTP 基类中直接调用深层嵌套的模板元函数;把校验逻辑拆到独立 trait 类里
- Clang 错误比 GCC 更友好,建议开发阶段用 Clang 编译调试
CRTP 看似只是“模板套一层”,但它的契约全靠程序员手动维护:类型传递、函数签名、const 正确性、模板实例化顺序——任何一环松动,编译器不会帮你兜底,只会抛出一长串不可读的模板错误。它不是替代虚函数的快捷方式,而是用编译期严格性换运行时自由度的明确取舍。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










