c++的CRTP模式是什么,有什么用? (实现静态多态)

CRTP是一种编译期确定的模板惯用法:派生类以自身为模板参数继承基类,实现零开销静态多态;它支持编译期类型检查、完全内联,但不支持动态多态或运行时行为。

CRTP 是什么:编译期确定的“假继承”

CRTP(Curiously Recurring Template Pattern)不是语言特性,而是一种模板编程惯用法:派生类以自身为模板参数继承基类。它让基类在编译期就能“知道”最终派生类型,从而实现静态多态——不靠 vtable、不产生虚函数调用开销。

典型写法长这样:

template 
class Base {
public:
    void interface() {
        static_cast(this)->implementation(); // 编译期绑定
    }
};

class MyConcrete : public Base { public: void implementation() { / 实际逻辑 / } };

为什么用 CRTP 而不用虚函数

核心动机是零成本抽象:避免运行时虚函数查表、禁止对象切片、支持 static_assert 在编译期校验接口契约。

  • 性能敏感场景(如数学库、嵌入式驱动)中,Base::interface() 可被完全内联,生成和直接调用 MyConcrete::implementation() 几乎等价的汇编
  • 基类可强制要求派生类提供特定成员(比如 value_typesize()),用 static_cast(this)->size() 触发 SFINAE 或编译错误
  • 无法用于多态数组或动态向上转型——CRTP 对象之间没有公共基类指针/引用关系,Base*Base* 类型完全不同

容易踩的坑:循环依赖与 this 指针安全

CRTP 最隐蔽的问题不是语法,而是派生类定义未完成时基类就试图访问其成员。

  • Base 构造函数里调用 static_cast(this)->xxx() 是未定义行为——此时 Derived 的构造函数还没开始执行,对象内存未就绪
  • 若基类模板中用到 Derived::some_nested_type,必须确保该类型在继承声明前已定义(常需前置声明 + 后续定义分离)
  • 不要试图在 CRTP 基类中存储 Derived* 并长期持有——生命周期管理责任不清,易悬垂

典型用途:表达式模板与混合类型操作

CRTP 真正发挥威力的地方,是需要在编译期组合类型行为的场景,比如 Eigen、xtensor 这类数值库。

例如实现向量加法不立即计算,而是构建表达式树:

template 
class VectorAdd : public Base> {
    LHS lhs_;
    RHS rhs_;
public:
    auto operator[](size_t i) const { return lhs_[i] + rhs_[i]; }
};

// 用户代码: auto expr = vec1 + vec2; // 类型是 VectorAdd,无临时对象、无虚调用 double x = expr[5]; // 到这里才真正计算

这种模式依赖 CRTP 让 Base 能统一处理所有表达式类型,同时保持每个具体表达式类型可区分、可优化。一旦涉及运行时决定行为(比如用户输入算子类型),CRTP 就不再适用——它只活在编译期。