C ++ CRTP初始化

时间:2018-10-09 15:10:28

标签: c++ initialization crtp

我遇到了运行以下程序的段错误

#include <iostream>
#include <vector>

template <typename Derived>
struct CRTPBase {
  CRTPBase() {
    func();
  }
  void func() {
    static_cast<Derived*>(this)->_func();
  }
};
struct CRTPChild : CRTPBase<CRTPChild>{
  using CRTPBase<CRTPChild>::CRTPBase;
  void _func(){
    vec.resize(10);
    vec[0] = 2;
  }
  std::vector<int> vec;
};
int main()
{
    CRTPChild obj;
    std::cout << obj.vec[0] << std::endl;
}

当我将vec替换为类型int的成员时,它将不再存在段错误。为什么?

3 个答案:

答案 0 :(得分:3)

调用CRTPBase构造函数时,CRTPChild尚未完全构造,因此调用其成员函数是未定义的行为。

未定义行为表现出来的方式取决于平台,编译器和月相。

特别是,当您的成员是一个int时,尚未构造它的事实并不会在您使用int时导致程序崩溃-int没有任何不变式。另一方面,向量具有不变量,因此访问未构造的向量将违反它们,并导致错误的内存访问。

答案 1 :(得分:3)

您的代码具有未定义的行为。问题来自order of the initialization;对于派生类CRTPChild,首先调用基类CRTPBase<CRTPChild>的构造函数,然后初始化vec的数据成员CRTPChild。从基类的构造函数调用_func时,vec根本没有初始化。

  

2)然后,直接基类按从左到右的顺序初始化为   它们出现在此类的基本说明符列表中

     

3)然后,按以下顺序初始化非静态数据成员   类定义中的声明。

将类型更改为int仍为UB。 UB意味着一切皆有可能,它可能导致段错误或可能不会。

答案 2 :(得分:2)

基类将在子类之前初始化(即构造)。这意味着当您调用CRTPChild::_func时,尚未构造对象的CRTPChild部分(包括向量)。以任何方式使用向量都会导致undefined behavior

不要在基类构造函数中访问子类的成员(非静态)。