This is an interesting write-up, but it leaves out some specifics in designing C++. A few are:

1. Concurrency

In garbage collected languages, the runtime can generally add objects to a free list and clean up memory when it's more-or-less convenient. Since C++ uses deterministic destruction and freeing, there has to be clear responsibilities defined. In particular, it has to be clear which thread is ultimately responsible for running the destructor. Point being, writing code that would otherwise be "abstract" in Java-land implies (but rarely explicitly defines!) some rigidity in design with respect to how long objects need to be around for different threads to use them.

Maybe that's too abstract. Here's some code to illustrate:

class IHandler {
public:
virtual Status handleAsync(const Msg & msg) = 0
};

class CopyingHandler : public IHandler {
private:
WorkerThread m_worker;

public:
Status handleAsync(const Msg & msg) override {
// capture the msg by value, taking a copy
return m_worker.push_back([=msg](){
handle(msg);
});
}
};

class RefHandler : public IHandler {
private:
WorkerThread m_worker;

public:
Status handleAsync(const Msg & msg) override {
// Capture the msg by reference. Assumes
// sticks around at least as long as the
// background thread does.
return m_worker.push_back([&msg](){
handle(msg);
});
}
};

Anyway, I left some details out, but you can see that both implementations adhere to the interface provided by IHandler, but each makes drastically different assumptions about what's OK. In some cases, problem domains, copying the message might create a performance bug. In most programs, you could easily (likely) have undefined behavior by capturing the message by reference, letting the message be destroyed, and then trying to use the message afterwards.

Point being, rigidity in C++ designs is easy to create subtly and accidentally, and even generic programming won't necessarily help you here.

2. Polymorphism

C++ actually allows for quite a range of polymorphic behaviors. You can't really rank them from highest rigidity to the least, since there are pros and cons to each approach, but generally inheritance is more rigid than the other approaches. In contrast, you can also have:

2a. Opaque Pointers

Not necessarily very type safe, but if you need to wrap something up in a black box, pass it around, then unwrap it and use it later (maybe in a message queue, a mailbox, a private implementation, or in certain kinds of IOC patterns), you can completely lose your type and recover it later. Typically there is a small performance hit as you use RTTI (https://en.wikipedia.org/wiki/Run-time_type_information) or a discriminator (an enum maybe) to decide how to recover your type when you're ready for it. See <experimental/any> (http://en.cppreference.com/w/cpp/experimental/any) for an example of this.

2b. Type Erasure

This one is more involved but clever use of generic programming lets us automatically provide the adaptor boilerplate to get types to play nice with other code. Sean Parent does an excellent walkthrough of this technique in his GoingNative talk in 2013 (https://channel9.msdn.com/Events/GoingNative/2013/Inheritanc...). Point being that type erasure lets you have a different flavor of flexibility in a way not seen in Java-land.

2c. Unions

This is harder to do properly in practice, as evidenced by the difficulty in standardizing something like boost::variant, but from time to time it might be worth playing around with various types of unions. They're a bit awkward, but you might try boost::variant if you like the tradeoffs it provides.

3. Value Semantics

Since objects are values in C++, you need to know the size of things much more often. This causes dependencies you might not think about in other languages, depending on how their standard libraries are designed.