在使用 CRTP 的情况下,派生的 class 方法是否会覆盖方法的基础 class 版本?
Does the derived class method override the base class version of the method in case the CRTP is used?
几天前,我发现了一个有趣的 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
.
万一编译器看到这个声明,这是一个信号,让他用参数 Circle
实例化 GraphicalObject
模板。我认为这会导致编译器
创建结构GraphicalObject<Circle>
struct GraphicalObject<Circle>
{
void draw()
{
static_cast<Circle*>(this)->draw();
}
};
因此创建了具有特殊版本 draw
方法的结构。结构 GraphicalObject<Circle>
构成 Circle
结构的基础结构。
在第二步中,创建 Circle
结构的实例 c
,即调用 Circle
的隐式构造函数。由于 Circle
是从 GraphicalObject<Circle>
继承的,因此首先调用该结构的隐式构造函数。在我看来,这会导致在 c
对象中创建 GraphicalObject<Circle>
的实例。这样对吗?然后我认为 Circle
结构中声明的 draw
方法覆盖了 GraphicalObject<Circle>
结构中声明的 draw
方法。但这对我来说没有意义。我希望来自 GraphicalObject<Circle>
的 draw
方法的“专业”版本不会被覆盖。请告诉我我对 CRTP 工作原理的理解有什么问题吗?
您没有在 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?
}
几天前,我发现了一个有趣的 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
.
万一编译器看到这个声明,这是一个信号,让他用参数 Circle
实例化 GraphicalObject
模板。我认为这会导致编译器
GraphicalObject<Circle>
struct GraphicalObject<Circle>
{
void draw()
{
static_cast<Circle*>(this)->draw();
}
};
因此创建了具有特殊版本 draw
方法的结构。结构 GraphicalObject<Circle>
构成 Circle
结构的基础结构。
在第二步中,创建 Circle
结构的实例 c
,即调用 Circle
的隐式构造函数。由于 Circle
是从 GraphicalObject<Circle>
继承的,因此首先调用该结构的隐式构造函数。在我看来,这会导致在 c
对象中创建 GraphicalObject<Circle>
的实例。这样对吗?然后我认为 Circle
结构中声明的 draw
方法覆盖了 GraphicalObject<Circle>
结构中声明的 draw
方法。但这对我来说没有意义。我希望来自 GraphicalObject<Circle>
的 draw
方法的“专业”版本不会被覆盖。请告诉我我对 CRTP 工作原理的理解有什么问题吗?
您没有在 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?
}