C++ does have language features that are heavily supported by the language runtime, including dynamic casts and exceptions. That support is not any better able to be patched to support future language evolution than Swift's runtime is; it's actually quite a bit less patchable in practice. The main reason C++ programmers don't have frequent back-deployment issues around these features is simply that C++ has largely abandoned the idea of evolving them at all, and most C++ programmers pretend that they do not exist.
There are a handful of other C++ features that technically require support from the language runtime, like aligned operator new
, but which are less integrated into other support. Formally these features also have back-deployment problems, but by their nature, I think programmers have found it easier to work around those limits. We've been able to do similar things in Swift for similar sorts of features, but not every feature cleanly fits that bill.
Dynamic casts are, not coincidentally, one of the biggest reasons we made the decision not to try to back-deploy the reflective / type metadata parts of parameterized protocol types. We could not find an acceptable way to back-deploy dynamic cast support for these types, so if we didn't do anything, casts were doomed to misbehave on old OSes. Intentionally introducing behavior that varied so wildly by target OS seemed much worse than locking down the things you could do with these types. And there's no way to prevent dynamic casts if you allow these types as generic arguments.