Reputation: 6029
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).)
Upvotes: 0
Views: 561
Reputation: 2568
The concrete problem you are mentioning is that you placed the friend
declaration in Base
rather than in Derived
.
The next problem is that Derived::Hello()
has no implementation. If you don't need one (as in your example above) implement it only in Base
and remove the virtual
declaration. Derived
will inherit it anyways.
The following works:
#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; }
};
Output:
Base::Factory()
Base::Base()
Derived::Derived()
Base::Hello()
Derived::~Derived()
Base::~Base()
Upvotes: 2
Reputation: 32727
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.
Upvotes: 3