> Inheritance is used to allow member functions declared in a parent class to be called in a member functions without requiring boilerplate code.
The defining characteristic of implementation inheritance is open recursion. When you call a "member function declared in a parent class" there's no telling what code will actually be run, because that member function may have been overridden at any point in the class hierarchy. There's no way of knowing whether the caller and callee code will agree on the required semantics and invariants involved, or even what these should be for any given case. That's why this is a misfeature for a "programming in the large" scenario.
By contrast, these issues can be managed when using implementation inheritance within a self-contained, smaller-scale program module, and then its open recursion behavior, properly managed, matches what we expect from the use of the typestate pattern.
> The defining characteristic of implementation inheritance is open recursion.
Recursion does not register as a concern in inheritance or polymorphism.
> When you call a "member function declared in a parent class" there's no telling what code will actually be run, because that member function may have been overridden at any point in the class hierarchy.
This is a feature, and a valuable one. You don't care what your parent class is doing. Your concern is that you want to extend it, and that's it.
> By contrast, these issues can be managed when (...)
You didn't pointed any issues. Also, composition addresses any concern you might have.
You're searching for problems that fit a solution, and so far you pointed no problem.
> composition addresses any concern you might have.
Agreed about this, of course. My approach is based on trying to describe what exactly it is that implementation inheritance adds beyond pure composition, and "open recursion", meaning late-bound dispatching through the 'this' or 'self' implied parameter (which is what potentially allows method calls anywhere in the hierarchy to dispatch to overridden methods in derived classes) is the pithy answer to that question.
The generic typestate pattern turns out to be a close analog in that it also involves function calls that go through a genericized "Self" object.
I do think that this leads to quite severe problems wrt. getting caller and callee code to agree on expected semantics in a large, multi-modular and potentially a quickly evolving codebase (which is what "programming in the large" means as a term of art) but then I've made that point above already and I'm not going to belabor it.
The defining characteristic of implementation inheritance is open recursion. When you call a "member function declared in a parent class" there's no telling what code will actually be run, because that member function may have been overridden at any point in the class hierarchy. There's no way of knowing whether the caller and callee code will agree on the required semantics and invariants involved, or even what these should be for any given case. That's why this is a misfeature for a "programming in the large" scenario.
By contrast, these issues can be managed when using implementation inheritance within a self-contained, smaller-scale program module, and then its open recursion behavior, properly managed, matches what we expect from the use of the typestate pattern.