Не могу использовать приватный конструктор класса CRTP - PullRequest
0 голосов
/ 27 марта 2019

У меня есть код, который использует этот дизайн, упрощенный, чтобы получить этот MCVE - код и ошибки компилятора следуют.

Основная проблема заключается в том, что я думал, что поддержка класса CRTP предоставит шаблонному базовому классу доступ к закрытым членам производного класса CRTP, включая его частный конструктор.

Но, видимо, это не так. Почему?

этот код на wandbox :

#include <iostream>
using namespace std;

template <class CRTP>
class Base
{
  friend CRTP;
public:
  static void Factory()
  {
      cout << "Base::Factory()" << endl;
      CRTP x;
      x.Hello();
  }
  virtual void Hello() { cout << "Base::Hello()" << endl; }
protected:
  Base() { cout << "Base::Base()" << endl; }
  virtual ~Base() { cout << "Base::~Base()" << endl; }
};


class Derived final : public Base<Derived>
{
public:
  void Hello() override;
private:
  Derived() { cout << "Derived::Derived()" << endl; }
  ~Derived() override { cout << "Derived::~Derived()" << endl; }
};

int main()
{
    Derived::Factory();
    // Expected output:
    //   Base::Factory()
    //   Base::Base()
    //   Derived::Derived()
    //   Derived::Hello()
    //   Derived::~Derived()
    //   Base::~Base()
}

И получение этой ошибки компилятора (из clang 9.0.0, но gcc жалуется так же):

prog.cc:12:12: error: calling a private constructor of class 'Derived'
      CRTP x;
           ^
prog.cc:33:14: note: in instantiation of member function 'Base<Derived>::Factory' requested here
    Derived::Factory();
             ^
prog.cc:27:3: note: declared private here
  Derived() { cout << "Derived::Derived()" << endl; }
  ^
prog.cc:12:12: error: variable of type 'Derived' has private destructor
      CRTP x;
           ^
prog.cc:28:11: note: declared private here
  virtual ~Derived() { cout << "Derived::~Derived()" << endl; }
          ^
2 errors generated.

(К сведению: вариант использования: я хочу, чтобы базовый класс шаблона управлял временем жизни (включая конструкцию) экземпляров производного класса (CRTP) через статическую фабрику. Поэтому я хочу, чтобы производные классы объявляли свои конструкторы частными, но доступными для метод родительской статической фабрики. В этом примере показан экземпляр производного класса, созданный в стеке, но возникают те же ошибки, если он создается в куче (и возвращается).)

Ответы [ 2 ]

2 голосов
/ 27 марта 2019

Конкретная проблема, о которой вы упоминаете, заключается в том, что вы поместили объявление friend в Base, а не в Derived.

Следующая проблема заключается в том, что Derived::Hello() не имеет реализации. Если вам не нужен (как в вашем примере выше), внедрите его только в Base и удалите объявление virtual. Derived все равно унаследует.

Следующие работы:

#include <iostream>

using namespace std;

template <class CRTP>
class Base
{
 public:
  static void Factory()
  {
    cout << "Base::Factory()" << endl;
    CRTP x;
    x.Hello();
  }
  void Hello() { cout << "Base::Hello()" << endl; }

 protected:
  Base() { cout << "Base::Base()" << endl; }
  virtual ~Base() { cout << "Base::~Base()" << endl; }
};

class Derived final : public Base<Derived>
{
  friend Base<Derived>;
/*^^^^^^ friend declaration goes here, not in Base */

  Derived() { cout << "Derived::Derived()" << endl; }
  ~Derived() override { cout << "Derived::~Derived()" << endl; }
}; 

Выход:

Base::Factory()
Base::Base()
Derived::Derived()
Base::Hello()
Derived::~Derived()
Base::~Base()
2 голосов
/ 27 марта 2019

Вы получили объявление friend в неправильном классе. Derived необходимо объявить Base<Derived> как друга, так как друг может получить доступ к закрытым членам. (Класс объявляет другой класс своим другом. Класс не объявляет себя другом другого класса.)

Вы хотите добавить

friend Base<Derived>;

в объявлении класса Derived.

...