Curiously Recurring Template Pattern (CRTP)——C++中继承自己的类?

发布时间 2023-04-20 00:59:25作者: Luke老黑

(或许应该翻译为[奇异的递归模板模式]?)

GPT的解释

最近在STL源码中看到此用法,查阅了很多资料。相较于大部分中文互联网的博客,ChatGPT给出的解释还是比较清楚的:

Curiously Recurring Template Pattern (CRTP)是一种C++编程技巧,用于实现静态多态行为。CRTP模式通过让一个基类模板继承自一个派生类模板,从而使得基类能够访问到派生类的成员变量和方法。
具体来说,CRTP模式是通过在一个模板类中将其自身作为另一个模板类的模板参数来实现的。例如,考虑以下代码:

template <typename T>
class Base {
public:
    void foo() {
        static_cast<T*>(this)->bar();
    }
};

class Derived : public Base<Derived> {
public:
    void bar() {
        // ...
    }
};

在这个例子中,基类模板Base中有一个名为foo()的方法。该方法使用static_cast将this指针转换为派生类类型,并调用派生类中的bar()方法。由于派生类Derived是Base的模板参数,因此可以从Base中访问Derived的成员函数和变量。
CRTP模式的优点是它提供了静态多态性(即,在编译时解决函数调用问题),同时避免了运行时多态性的开销。例如,如果将foo()和bar()声明为虚拟函数,则必须使用虚拟函数表来解析调用,这可能会导致性能损失。
总之,CRTP模式是一种强大的C++技术,可以帮助我们实现静态多态性和高效的代码。

划重点:此方法提供了静态多态性。减少动态多态性时解析虚函数表的开销。

这种静态多态性如何实现?

我们还是使用刚才GPT给出的程序来观察程序是如何运行的。

在上面的例子中,虽然类模板Base首先被声明,但其实并没有被实例化(注意和对象实例化的区别)。也就是说,这个时候并没有在代码区(.text)实际生成类的成员函数相关代码,也就不需要知道类的成员函数的实际大小(实际此时也无法确定)。

直到Derived类声明后,我们对相关代码的调用(如下),这种实例化才会开始被编译器执行。我们都知道,在编译时确定的多态被称为静态多态。

    Base<Derived> b; /** 此时进行模板实例化 */
    b.foo();

Base<Derived>这样的形式似乎有点让人费解,但我们可以这样简单理解“父类Base知道了子类Derived的具体形式后,就可以执行子类的动作了”。这时候我们对比使用虚函数实现动态多态,即“当父类指针知道其指向的子类对象后,就可以执行子类的动作了”。两者是非常相似的,也就不难理解为什么可以用CRTP方法替代动态多态。

————待补充应用,未完————