Your criticisms would be well taken if they were actually true:

>Exceptions can be used, except that they cannot propagate out of destructors

Exceptions are perfectly well allowed to propagate out of destructors. A destructor throwing an exception is only considered ill advised, because destructors are called during stack unwinding when another exception is propagating, and throwing an exception during another exception causes program termination. And object destruction is largely incapable of truly failing anyway: There will be no state remaining to be left inconsistent because the object is going away, unless the destructor is interacting with an object not being destroyed, in which case the error is actually with the other object and information about the error can be stored with that object or in an error queue and handled in due course after the stack unwinding is complete.

Moreover, what's your solution to the problem? If the destructor is doing nothing but deallocating memory (and calling other destructors that only deallocate memory) then it never needs to throw and there is no problem. If it's doing something else then it's either doing something that another language wouldn't allow because it has no destructors (in which case you can refrain from doing it in C++ as easily as you can switch to one of those languages), or the other language has an equivalent to destructors and then will have the same issue with throwing during exception propagation. Do you see some solution to having it both ways?

>they should not propagate out of constructors

Rubbish. Constructors don't even have return values. Throwing exceptions is the canonical way of indicating construction failure.

The primary benefit to non-throwing constructors is that it allows certain performance optimizations. For example, if a constructor throws during a std::vector resize operation, the vector class will undo everything that had been done during the resize operation so as to leave the vector in a consistent state. This is easy with copy constructors: Just destroy the copies and keep the originals. But if vector used the move constructor to move the existing elements to the newly allocated internal array then the originals are no longer valid. So vector's resize will only use the move constructor instead of the copy constructor if the move constructor is declared noexcept (or the compiler can statically determine that it doesn't throw), since the alternative would violate the vector's ability to maintain consist state when a move constructor throws an exception.

>there is no standardized way to retry the operation that threw the exception.

How about catching the exception, addressing it, and retrying the operation in the try block?

What do other languages do that you feel is superior?

> The default numeric type in C++ is fixed-width (int or floating point)

Why do you believe this to be a serious limitation? The range of a 64-bit type is more than sufficient for the overwhelming majority of applications and for the few remaining with specialized needs (you know who you are, cryptographers and mathematicians), arbitrary precision libraries are readily available.

> the default string type is a primitive pointer (const char *)

The default string type is std::string. Which can even be used with the large bulk of the C library through the helper function string::c_str() which provides a "safe" temporary const char array for passing to C library functions.