Ah. Dyno (https://github.com/ldionne/dyno) is good at this stuff. If you have types Base, Child1, Child2, you can just return a Dyno object that can be any of these, expressed as a tagged union and not a Box-equivalent, and then do regular vtable-based or otherwise polymorphic dispatch into the object. You can also arrange it so that if you have a Child3 that can't fit in the (Base, Child1, Child2) union, the Child3 can be heap-allocated and invoked transparently as well. It's open-world type erasure.
C++ is so freakishly powerful is that it can not only solve this problem, but it can solve it via a regular library and not a language extension.
Dyno is really freakish and I can see a couple of pain points if someone tries to use it productively. A IMO glaring one is that dyno::poly is a boxing type that will happily take your value type and shove it on the heap while nobody's looking. This is a defect similar to the silent heap allocations for captured parameters in std::function.
On the other hand, it's really impressive that one can push dynamic function pointer lookup tables in C++ to a point where it looks almost, but maybe not quite like the real thing.
C++ is so freakishly powerful is that it can not only solve this problem, but it can solve it via a regular library and not a language extension.