派生模板类对象的实例化



>我有类模板充当派生类的基类。这个想法是通过CRTP技巧利用"静态多态"。

#include <iostream>
template <typename T>
class BASE
{
public:
void write() {static_cast<T*>(this)->write(); }
};
class DER1 : public BASE<DER1>
{
public:
void write(){
std::cout << "Calling write() inside DER1 " << number << std::endl;}   
private:
int number = 11;
};

我尝试了 2 种不同的方法来实例化派生类对象,并且发现这两种方法中的一种不正确。但我不明白为什么。

int main(void) {
BASE<DER1> der1_objA ;
der1_objA.write();
DER1 der1_objB ;
der1_objB.write();
return 0;
}

事实上,我得到作为输出

Calling write() inside DER1 1880535040    [ random number]
Calling write() inside DER1 11            [correct number ]

谁能解释一下问题出在哪里? 提前非常感谢你。

当你定义一个类型为 BASE 的对象时,它只是一个BASE,但在它里面,你可以将 this 指针指向它不是的东西 (DER1(,然后继续通过该无效指针使用它。 这是未定义的行为,垃圾是正常后果。 CRTP 工作的唯一时间是对象的动态类型实际上是传递给基类的模板参数。 也就是说,如果 BASE 认为它真的是 DER1,那么它一定是 DER1。 当 DER1 仅为 BASE 并将该指针用于 DER1 操作时,将自身强制转换为 DER1 是未定义的行为,与执行此操作没有太大区别:

int x = 42;
std::string * sptr = (std::string*)&x; // extremely questionable
sptr->clear(); // undefined behavior

您应该考虑使 BASE 构造函数具有"受保护"的访问级别,以防止简单的滥用情况。 这样,基构造函数只能由派生对象调用,因此您不会意外地孤立地实例化基:

template <typename T>
class BASE
{
protected:
BASE() = default;
BASE(BASE const&) = default;
public:
void write() {static_cast<T*>(this)->write(); }
};

然后,您不会得到垃圾,而是:

BASE<DER1> objA ;
objA.write();
error: 'BASE<T>::BASE() [with T = DER1]' is protected within this context
BASE<DER1> base;

BASE<DER1>实例中,没有DER1实例,而只有一个试图将自己转换为它不是的东西的BASE

PS:这个关于 c++ 内存模型的讨论非常相关,但超出了我可以用简单的话解释的范围。

最新更新