0Pricing
C++ Academy · 课时

何时使用 CRTP

权衡与陷阱

何时使用 CRTP 是 CoddyKit 上的免费 C++ Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C++ Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C++ Academy 课程共包含 4 节课。

强大但需要谨慎使用的工具

CRTP 功能强大,但会使代码更加复杂。本课将介绍它何时值得使用,以及需要避免的陷阱。

适用场景:热点路径

当消除虚函数调用开销很重要时,请使用 CRTP:例如紧凑的数值循环、表达式模板,或类型在编译时固定的解释器。

适用场景:可复用行为

CRTP 擅长注入可复用行为(计数器、比较、序列化),尤其适用于这些行为必须回调具体类型的情况。

#include <iostream>
#include <string>

template <typename T>
struct Serializable {
    void save() const { std::cout << static_cast<const T*>(this)->serialize() << "\n"; }
};

struct Config : Serializable<Config> {
    std::string serialize() const { return "config-data"; }
};

int main() {
    Config{}.save();
    return 0;
}

陷阱:没有公共运行时类型

如果调用方需要一个统一的运行时类型来一起存储多个形状,CRTP 无法提供帮助。每次实例化都是一个不同的类型。

  • 需要多态容器吗?请使用虚函数

陷阱:晦涩的错误

CRTP 的错误消息可能很长且令人困惑,因为错误会在模板实例化的深层位置暴露出来。在 Base<Wrong> 中误写派生类型时,代码可能一直编译成功,直到某处调用了缺失的方法。

陷阱:错误的派生类型

向基类传入错误的类型会破坏 static_cast 契约,并导致未定义行为。请始终按照 Derived : Base<Derived> 的形式书写,并确保名称匹配。

#include <iostream>

template <typename T>
struct Base {
    void go() { static_cast<T*>(this)->step(); }
};

struct Right : Base<Right> {
    void step() { std::cout << "ok\n"; }
};

int main() {
    Right{}.go();
    return 0;
}

替代方案:std::variant

当您拥有一组固定的类型并希望使用值语义时,std::variant 加上 std::visit 可以在不使用继承的情况下提供编译时分派,而且通常比 CRTP 更清晰。

#include <iostream>
#include <variant>

struct Add { int operator()(int a, int b) { return a + b; } };
struct Mul { int operator()(int a, int b) { return a * b; } };

int main() {
    std::variant<Add, Mul> op = Mul{};
    int r = std::visit([](auto f) { return f(3, 4); }, op);
    std::cout << r << "\n";
    return 0;
}

替代方案:概念(C++20)

对于纯粹的编译时接口检查,C++20 的 concepts 通常可以替代 CRTP。它们能够约束模板,并在不使用继承的情况下提供更清晰的错误消息。

可读性权衡

CRTP 增加了模板机制,初学者可能难以读懂。请仅在性能或复用方面的收益明显超过额外复杂度时使用它。

维护方面的考虑

CRTP 会紧密耦合基类与派生类。重构宿主接口可能波及每个混入类。请清楚地记录宿主类所需的方法。

  • 在注释中列出所需的方法
  • 优先使用小型且职责集中的混入类

决策检查清单

请在以下情况下使用 CRTP:

  • 需要通过静态分派来提升速度
  • 需要注入会调用具体类型的行为
  • 不需要共享的运行时基类
  • 复杂度是合理的

否则,请考虑虚函数、变体或概念。

快速检查

当您需要共享的运行时类型时,请选择最佳替代方案。

回顾

您学会了判断 CRTP 的适用场景。

  • 非常适合热点路径和可复用的回调行为
  • 不适合需要公共运行时类型的场景
  • 请注意晦涩的错误以及错误类型的误用
  • 替代方案:虚函数、std::variant、C++20 概念

常见问题解答

「何时使用 CRTP」课时是免费的吗?

是的 — 「何时使用 CRTP」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C++ Academy 课程的其余内容,请升级到 CoddyKit PRO。 C++ Academy 课程共包含 4 节课。

「何时使用 CRTP」这节课中我会学到什么?

权衡与陷阱 你通过在浏览器中直接运行的动手代码来练习 C++ Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 C++ Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 C++ Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「何时使用 CRTP」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 C++ Academy 课中编写并运行代码吗?

能。每节 C++ Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. CRTP 惯用法
  2. 静态多态
  3. 使用 CRTP 实现混入
  4. 何时使用 CRTP
← 返回 C++ Academy