No matter which implementation sharing technique you choose, you must still connect the implementation class with the type-specific operations. There are several ways to achieve this:
The implementation defines pure virtual functions for the type-specific operations. These are overridden by a derived class template. (This is what is done in the private inheritance case.)
The implementation is given a pointer or reference to an object which has virtual methods for the type-specific operation. (This is what happens in the following example.)
The implementation is given a set of pointer-to-member functions to the type-specific operations.