.. which makes using the STL surprisingly hard, because you can get an out of memory exception all over the place.
In practice, I think this issue gets far more attention than it deserves, particularly within the C++ community. If you’re running in a particularly resource-constrained environment where running out is a relevant problem, you probably already take precautions about how you allocate those resources and adjust how you structure your program accordingly. You’re trying to avoid any possibility of those errors occurring, which means you don’t then have to worry about dealing with them unexpectedly all over your code.
For me, the more interesting discussing is around how we deal with errors that can be properly recognised and handled within normal execution. That means the interesting exceptions are those we throw ourselves and those deliberately thrown by other modules that we depend on, which we hope will be documented with each module’s interface. These things don’t just go off arbitrarily in just about any line of code we write, and they can be systematically structured, and we can write code that controls where mutations and other effects happen and where exceptions happen and how they interact.
Exceptions with mutable state needs to be coded transactionally, yes, and this is an excellent reason to try and avoid mutable state and use persistent data structures where possible.