crtp是c++中实现零开销、无虚表、编译期确定调用路径的唯一工程级多态方案:需定义接收派生类类型的模板基类,派生类显式传入自身完整类型,所有派生类调用通过static_cast(this)完成,严格禁止虚继承、多继承及构造函数内调用。

要在C++中实现零开销、无虚函数表、调用路径在编译期完全确定的多态行为,必须放弃运行时类型分发,转而依赖模板特化与静态绑定——CRTP(奇异递归模板模式)是唯一满足该目标的工程级方案,它要求派生类主动将自身完整类型作为模板参数传入基类,使所有成员访问在实例化阶段就完成解析。
定义带模板参数的CRTP基类并强制static_cast调用
基类不能是普通类,必须是显式接收派生类类型的模板;所有对派生类逻辑的调用,都必须通过static_cast<derived>(this)</derived>完成,这是编译期绑定的物理基础。
第一步:声明模板基类,仅接受一个类型参数Derived,不设默认值,避免误用:
template
第二步:在公有接口函数中,使用static_cast<derived>(this)->draw_impl()</derived>调用派生类实现;禁止在构造函数体内部调用该转换后的成员函数,【此时派生类数据成员尚未构造,访问即未定义行为】。
第三步:为关键调用点添加static_assert校验,例如:static_assert(std::is_member_function_pointer_v<decltype>, "Derived must define draw_impl()");</decltype>
这能将错误定位从static_cast行提前拉回基类定义处,避免排查绕弯。
派生类显式传入自身完整类型并实现约定接口
这一步不是语法习惯,而是编译器生成可执行代码的硬性前提:模板实参必须闭环,否则Base<derived></derived>无法实例化,整个继承链立即断裂。
方法一:非模板派生类直接写全名
class Circle : public ShapeBase
方法二:若派生类自身是模板(如template<typename t> class Vec3</typename>),则继承时必须传入特化后的完整类型
class Vec3f : public ShapeBase
写成ShapeBase<vec3></vec3> ❌ —— Vec3是模板名,不是类型,触发incomplete type错误。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
方法三:禁用任何占位或推导写法class X : public ShapeBase<void></void> ❌class X : public ShapeBase<auto></auto> ❌class X : public ShapeBase ❌ —— 编译器报错:use of class template 'ShapeBase' requires template arguments。
规避多重/虚继承导致的static_cast失效风险
CRTP的安全性全部建立在标准对象布局之上:派生类子对象地址必须与基类子对象起始地址一致。一旦破坏该前提,static_cast<derived>(this)</derived>就不再是安全偏移,而是未定义行为。
只允许public单继承,禁止私有或保护继承——否则编译器报错:conversion from 'ShapeBase*' to 'Derived*' is inaccessible。
绝对禁止虚继承或多继承(尤其是当CRTP基类非首基类时):
class Bad : public ShapeBase
此时this指针指向OtherBase子对象起始地址,static_cast<bad>(this)</bad>读取draw_impl()会越界到相邻内存,Clang/GCC开启-Wall会警告但不拦截。
若需组合功能,请改用成员对象组合(has-a)而非继承(is-a),或提取为独立策略模板,避免污染CRTP继承链。
用if constexpr实现编译期条件分支
当不同派生类需在统一接口中执行差异化逻辑时,绝不可用typeid或运行时if,必须用if constexpr让编译器在实例化时裁剪掉无效分支。
将分支逻辑封装在基类模板成员函数中,例如:
template
void render() {
if constexpr (std::is_same_v
static_cast
} else if constexpr (std::is_same_v
static_cast
}
}
注意:每个分支内的表达式必须在对应Derived类型下具备完整定义,否则未选中分支也会因符号缺失而编译失败;if constexpr只能用于模板函数或模板成员函数内,普通函数中使用会报错。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










