davidbak
davidbak

Reputation: 6029

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

Upvotes: 0

Views: 561

Answers (2)

dtell
dtell

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

1201ProgramAlarm
1201ProgramAlarm

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

Related Questions