派生模板 class 对象的实例化

Instantiation of derived template class objects

我有 class 模板作为派生 class 的基础 class。这个想法是通过 CRTP 技巧利用 "static polymorphic"。

#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;
};

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

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<DER1> 实例中没有 DER1 实例,只有一个 BASE 试图将自己转换成它不是的东西。

PS: this talk 关于c++ 内存模型非常相关,但超出了我可以用简单的语言解释的范围。

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

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

您应该考虑使 BASE 构造函数具有 "protected" 访问级别,以防止简单的误用情况。这样基础构造函数只能被派生对象调用,所以你不会不小心在隔离中实例化基础:

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;