Put all your cleanup in destructors. All of it. They exercise that code all the time, on every run. Then, throwing always does the right thing, with no extra effort.
You can do pretty OK Maybe/Either types in modern C++, but there’s not an obvious way to get the ergonomics you want.
At the time this was written the right move was usually to signal failure with a return value and return your successful value with an out-parameter.
Java can do exceptions with VM/JIT support and it’s much more predictable, but doesn’t hit large parts of the domain. Anything north of Java in runtime support is targeting non-overlapping domains to C++ in 2022.
Rust is doing a somewhat exciting experiment with custom syntax for Maybe/Either. It’s a bit soon to say how it will age. I fear it won’t turn out to be general enough, but it beats the hell out of errno.