Как создать несколько объектов с CRTP? - PullRequest
1 голос
/ 21 октября 2019

Я пишу небольшой пример шаблона CRTP, чтобы лучше изучить его и использовать его в более сложном коде. Я хочу использовать CRTP, этот базовый класс имеет доступ к производному классу. Все хорошо, но я не могу создать несколько объектов для своего базового класса. Если я при первом вызове конструктора для обоих объектов Base<Derived1> base1; Base<Derived2> base2;, а при втором вызове функции из каждого объекта base1.PrintDerived_FromA(); base2.PrintDerived_FromA();, у меня будет результат:

Base constr work
Base constr work
b_: 0
b_: 25

, но у меня должно быть:

Base constr work
Base constr work
b_: 9
b_: 25

Если я вызываю функцию сразу после конструктора, все в порядке:

Base<Derived1> base1;
base1.PrintDerived_FromA();
Base<Derived2> base2;
base2.PrintDerived_FromA();

Результат:

Base constr work
b_: 9
Base constr work
b_: 25

Оказывается, новый вызов конструктора переопределяет существующий объект, но почему? Это можно исправить? И я хочу использовать только CRTP, без виртуальных функций.

#include <iostream>

template <class T>
class Base {
 public:
  Base();
  void PrintDerived_FromA();
  void InitializeDerived();
};

class Derived1 : public Base<Derived1> {
 public:
  Derived1(int b);
  void PrintDerived();
  void SetDerived(int b);

 private:
  int b_;
};

class Derived2 : public Base<Derived2> {
 public:
  Derived2(int b);
  void PrintDerived();
  void SetDerived(int b);

 private:
  int b_;
};


template <typename T>
Base<T>::Base() {
  InitializeDerived();
  std::cout << "Base constr work" << std::endl;
}

template <>
void Base<Derived1>::InitializeDerived() {
  static_cast<Derived1*>(this)->SetDerived(9);
}

template <>
void Base<Derived2>::InitializeDerived() {
  static_cast<Derived2*>(this)->SetDerived(25);
}

template <typename T>
void Base<T>::PrintDerived_FromA() {
  static_cast<T*>(this)->PrintDerived();
}


Derived1::Derived1(int b) : b_(b), Base() {
  std::cout << "Derived1 constr work" << std::endl;
}

void Derived1::PrintDerived() {
  std::cout << "b_: " << b_ << std::endl;
}

void Derived1::SetDerived(int b) {
  b_ = b;
}


Derived2::Derived2(int b) : b_(b), Base() {
  std::cout << "Derived2 constr work" << std::endl;
}

void Derived2::PrintDerived() {
  std::cout << "b_: " << b_ << std::endl;
}

void Derived2::SetDerived(int b) {
  b_ = b;
}


int main() {
  Base<Derived1> base1;
  Base<Derived2> base2;

  base1.PrintDerived_FromA();
  base2.PrintDerived_FromA();

  return 0;
}

1 Ответ

1 голос
/ 21 октября 2019

static_cast<Derived1*>(this) приведение неверно: this указывает на объект типа Base<Derived1>, а не на Derived1. Таким образом, разыменование созданного указателя приводит к неопределенному поведению. Чтобы CRTP работал, вам нужно создать объекты производных классов.

...