派生模板类对象的实例化

Posted

技术标签:

【中文标题】派生模板类对象的实例化【英文标题】:Instantiation of derived template class objects 【发布时间】:2017-08-25 17:44:23 【问题描述】:

我有类模板作为派生类的基类。这个想法是通过 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&lt;DER1&gt; 实例中没有DER1 实例,但只有一个BASE 试图将自己投射到它不是的东西 最后是切片。 @tobi303 答案在这里!! :-) ...我找不到比你更好的措辞了。 @skyjack 我也没有:P 【参考方案1】:

当你定义一个 BASE 类型的对象时,它只是一个 BASE,但在其中你将 this 指针指向它不是的东西(DER1),然后继续使用它通过那个无效指针。那是未定义的行为,垃圾是正常的结果。 CRTP 唯一起作用的时候是对象的动态类型实际上是传递给基类的模板参数。也就是说,如果 BASE 认为它真的是 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;

【讨论】:

【参考方案2】:

BASE&lt;DER1&gt; 实例中没有DER1 实例,而只有一个BASE 试图将自己转换为它不是的东西。

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

【讨论】:

以上是关于派生模板类对象的实例化的主要内容,如果未能解决你的问题,请参考以下文章

为啥可以从指向实例化基类对象的强制转换指针调用非静态派生类方法?

C++多态模板类,模板类型对象的实例化

我无法覆盖基类的方法,因为我的派生类是模板化的

具有模板化返回类型的虚拟成员函数的实例化

cython c++ 类继承不能实例化派生类

多态和抽象