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?
#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).)
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.