Here's a toy example illustrating a problem I encounter. The application is fairly irrelevant (it's essentially a linked list of elements with a special behavior at the end). I'm unable to construct a base class shared_ptr with a derived pointer and it's for some reason linked to the fact that I'm using private inheritance.
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
using namespace std;
// An Item in a linked list
class A
{
public:
  //friend class B;
  typedef boost::shared_ptr<A> APtr;
  A() : next_() {}
  A(APtr n) : next_(n) {}
  APtr next() { return next_; }
  void setNext(APtr n) { next_ = n; }
  virtual void doIt() { /* standard behavior */ }  
private:
  APtr next_;
};
class B : private A // B really is a special A
                    // that should have different behavior
                    // at the tail of the chain
                    // but I want to hide A's interface
                    // to external clients
{
public:
  typedef boost::shared_ptr<B> BPtr;
  B(A::APtr prev) 
  { // Set this object as the tail
    prev->setNext(APtr(this));  /* WHY CAN'T I CONSTRUCT APtr(this) 
                                   WITH PRIVATE INH. */
  }
  void doIt() {/*special behavior at end */}
};
int main()
{
  A::APtr dummyPtr;
  A::APtr head = boost::make_shared<A>(dummyPtr);
  B::BPtr tail = boost::make_shared<B>(head);
  for(A::APtr curr = head; curr; curr=curr->next()){
    curr->doIt();
  }
  return 0;
}
and I get this
/usr/include/boost/smart_ptr/shared_ptr.hpp: In constructor ‘boost::shared_ptr<T>::shared_ptr(Y*) [with Y = B, T = A]’:
derived_shared.cpp:31:   instantiated from here
/usr/include/boost/smart_ptr/shared_ptr.hpp:352: error: ‘A’ is an inaccessible base of ‘B’
I was under the impression that private inheritance allows the Derived class to still access the public interface of the base class but hides that interface to external clients. Why does private inheritance cause this error (it works if I inherit publicly)?
                        
Change this single line:
to
And it compiles.
Or at least it does when using the
stdlibrary. It is usually similar forboost.There are other errors but that gets round casting
B*toA*.Why does that work? Because the template for the constructor
std::shared_ptr<A>isn't what you think it is! It is more liketemplate <class X> std::shared_ptr(X* v). So the actualB*toA*cast is postponed and failing in a non-friend member.But if you cast the
B*pointer (i.e.this) toA*inside a method ofclass B(the only place that is legal without afrienddeclaration) you're in.NB: There is nothing in principle wrong with private inheritance. It isn't an anti-pattern and is provided for good reason. Do think about composition but objects that forbid some parts of the application from 'accessing' their 'real' type have lots of uses. For example pass out an object A that has some B bolt ons that only the object factory can access.
PS: The reason why the constructor is
template<class T> shared_ptr<T* v>is so thatshared_ptruses the deleter of the type passed into it. As you're not doubt awareshare_ptrcleverly calls the 'right' destructor even if it isn't virtual. My 'fix' actually subverts that cleverness so beware to pass in the right deleter or (recommended) make the destructor ofAvirtual.PPS:
And finally a fully working program (using STL. Sorry I don't have Boost):
You need to use
enable_shared_from_thisbecause otherwise you're trying to create two 'families' ofshared_ptrand that won't work.I've made a factory method because fixing the constructor just wouldn't work! There is a precondition of
enable_shared_from_thisthat there has to be astd::shared_ptrin existence and I suppose that means 'fully constructed'.The following constructor will not work for me:
That said, if you do inherit from
enable_shared_from_thisit's a good idea to make all the constructors private and provide factories that returnshared_ptr. Otherwise you can get in a right mess if calling code doesn't itself ensure that 'pre-existingshared_ptr' condition. A nasty piece of coupling if ever there was one.