为什么奇异递归模板模式(CRTP)能够生效?
奇异递归模板模式(CRTP)的生效原理与起源背景
我太理解你这种翻遍资料却找不到核心逻辑的困惑了!当初我研究CRTP的时候也卡在这个点上,先结合你提到的那段起源历史,再把它的生效原理掰碎了说清楚:
一、CRTP的“意外诞生”
- 1995年,Jan Falkin在开发微软ATL(Active Template Library)时独立发现了CRTP的实现方式——说起来很有戏剧性,他是意外地让基类从派生类继承,完全是误操作带来的“惊喜”。
- 当时Christian Beaumont看到这段代码时,第一反应是“这在当时的微软编译器里根本编译不过”,毕竟这种“基类依赖派生类”的写法看起来完全违背常规继承逻辑,但实际测试后发现居然能正常运行,后来干脆把整个ATL和WTL(Windows Template Library)的核心设计都基于这个模式来构建了。
二、CRTP为什么能生效?
核心原因在于C++模板的“延迟实例化”特性:
- 当你写出CRTP的典型代码:
编译器在处理template <typename Derived> class Base { public: void do_something() { static_cast<Derived*>(this)->implementation(); } }; class Derived : public Base<Derived> { public: void implementation() { // 具体实现逻辑 } };Base<Derived>这个模板的时候,并不会立刻去解析Derived的所有细节——因为模板是在被使用时才会实例化,而不是在定义的时候。 - 当
Derived类被定义时,它继承的Base<Derived>此时只是一个“未完全实例化”的模板;直到你调用do_something()这类成员函数时,编译器才会去实例化Base<Derived>的具体成员,这时Derived的完整定义已经被编译器看到了,所以static_cast<Derived*>(this)是合法的。 - 简单说:CRTP利用了模板实例化的时机差,让基类在真正需要用到派生类的细节时,派生类已经完成了定义,从而避开了“循环依赖”的编译错误。
另外要注意:这种写法只在模板语境下成立,如果是非模板的类这么写(基类直接继承未定义的派生类),那肯定会编译失败——这也是为什么当初Beaumont一开始觉得代码跑不起来的原因,毕竟常规继承逻辑里这种写法完全不合理,但模板的延迟实例化给了它生存空间。
内容的提问来源于stack exchange,提问作者Soup Endless
相关产品推荐
相关产品推荐

