首页 > 解决方案 > 如果使用 CRTP,派生类方法是否会覆盖该方法的基类版本?

问题描述

几天前,我发现了一个有趣的 C++ 结构,名为Curiously Recurring Template Pattern(通常缩写为 CRTP)。从那时起,我一直试图完全理解这个技巧是如何工作的。

假设我有以下代码片段

#include <iostream>

template<class T>
struct GraphicalObject
{
    void draw()
    {
        static_cast<T*>(this)->draw();
    }
};

struct Circle : GraphicalObject<Circle>
{
    void draw()
    {
        std::cout << "Drawing circle!" << std::endl;
    }
};

struct Square : GraphicalObject<Square>
{
    void draw()
    {
        std::cout << "Drawing square!" << std::endl;
    }
};

int main(int argc, char** argv) {
    
    Square s;
    Circle c;
    
    s.draw();
    c.draw();
    
    return 0;
}

CRTP 背后的所有“魔力”显然都在行static_cast<T*>(this)->draw()。但它是如何详细工作的?我的理解如下。假设我有Circle c.

如果编译器看到这个声明,这是他GraphicalObject用参数实例化模板的信号Circle。我认为这会导致GraphicalObject<Circle>编译器创建结构

struct GraphicalObject<Circle>
{
 void draw()
 {
  static_cast<Circle*>(this)->draw();
 }
};

draw因此创建了具有该方法的特殊版本的 struct 。该结构GraphicalObject<Circle>构成该结构的基础Circle结构。

在第二步中创建结构的实例c,即调用隐式构造函数。由于首先调用的是从该结构的隐式构造函数继承的事实。在我看来,这会导致创建对象内部的实例。这样对吗?然后我认为struct中声明的方法覆盖了struct中声明的方法。但这对我来说没有意义。我宁愿期望来自 的方法的“专业”版本不会被覆盖。请你能告诉我我对 CRTP 工作原理的理解有什么问题吗?CircleCircleCircleGraphicalObject<Circle>GraphicalObject<Circle>cdrawCircledrawGraphicalObject<Circle>drawGraphicalObject<Circle>

标签: c++inheritancecrtpstatic-polymorphism

解决方案


你没有做任何多态的事情main来展示 CRTP 的特性。试试这个,看看会发生什么:

template <typename T>
void drawObject(GraphicalObject<T>& obj) {
    obj.draw();
}

int main() {
    Square s;
    Circle c;
    
    s.draw();
    c.draw();

    drawObject(s);  // what happens here?

    GraphicalObject<Circle>& c_ref = c;
    drawObject(c_ref);  // what happens here?
}

推荐阅读