Search code examples
c++friendcrtp

Can't use private constructor of befriended CRTP class


I have code which uses this design, simplifed to get this MCVE - code and compiler errors follow.

The basic problem is that I thought befriending the CRTP class would allow the templated base class access to the derived CRTP class's private members, including its private constructor.

But apparently it doesn't. Why?

this code on 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()
}

And getting this compiler error (from clang 9.0.0, but gcc complains the same way):

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.

(FYI: Use case is I want the template base class to control lifetime - including construction - of the (CRTP) derived class instances via a static factory. So I want the derived classes to declare their constructors private, yet accessible to the parent's static factory method. This example shows the derived class instance created on the stack but the same errors occur if it is created in the heap (and returned).)


Solution

  • You've got the friend declaration in the wrong class. Derived needs to declare Base<Derived> as a friend, since a friend can access the private members. (A class declares another class to be a friend. A class does not declare itself as a friend of another class.)

    You want to add

    friend Base<Derived>;
    

    into the Derived class declaration.