Consider a CRTP pattern template <class DerivedType> class Base. I would like to:
Base class cannot be instantiated
(Some) methods in Derived class can called only through the "interface" of the Base class
Base class can access to only some of the private members of Derived
To satisfy 1., it is enough to declare the constructor of the Base class protected. To satisfy 2., it is possible to make the methods private, and declare the Base class friend. This will however allow Base to access all members of Derived.
See for example this code:
#include <iostream>
template <class T>
class Base {
protected:
Base() = default;
public:
void interface() const { static_cast<const T*>(this)->do_something(); }
// This should not compile
void interface_to_private() const { static_cast<const T*>(this)->do_something_private(); }
};
class Derived : public Base<Derived> {
friend class Base<Derived>;
void do_something() const { do_something_private(); }
// These should be not available to Base<Derived>
int i = 1;
void do_something_private() const { std::cout << "hello " << i << std::endl; }
};
int main()
{
Derived d;
d.interface();
d.interface_to_private(); // This should give error
return 0;
}
In the example code I would like Base<Derived> to have access to do_something(), but not to do_something_private(), nor to the member field int i.
I am aware that granularity of friend access is a known problem, that can, for example, be solved by the Attorney-Client idiom. However, I was not able to figure out if it can be really combined to the CRTP idiom. It goes without saying, that I do not want virtual members (this is the whole point of using CRTP).
I found a solution by hiding the methods in another class:
#include <iostream>
template <class T>
class Base {
protected:
Base() = default;
public:
void interface() const { static_cast<const T*>(this)->do_something(); }
// This should not compile
void interface_to_private() const { static_cast<const T*>(this)->do_something_private(); }
};
class Implementation {
int i = 1;
void do_something_private() const { std::cout << "hello " << i << std::endl; }
protected:
Implementation() = default;
public:
void do_something() const { do_something_private(); }
};
class Derived : public Base<Derived>, private Implementation {
friend class Base<Derived>;
};
int main()
{
Derived d;
d.interface();
// uncommenting gives compile error
// d.interface_to_private();
return 0;
}
Uncommenting the line d.interface_to_private(); cause a compilation error.
While this appears to work, it seems involved, and I wonder if it is a good solution given that multiple inheritance is generally frowned upon.
Is there a better solution, or a known "idiom" for this case?