As a general rule, use them when for truly exceptional situations: Running out of memory, accessing something out of bounds - things that you are checking for anyway, but you don't or don't want to use a return-value-based error handling system for.
In modern C++ this is already the case. For example, every container that implements an at() function will throw an exception if the (unsigned) index is >= size.
If you look at the assembly of a simple function only accessing a std:array using the [] operator, you will see that it's quite small. If you access it using at() you might see that the core function remains the same, except it now has a bounds-checking test which jumps to a huge postamble. The postamble will throw the exception. Since the CPU likes to execute instructions in sequence, we say that the happy path (which is indexing correctly) is now the fast path.
Why use exceptions for unrecoverable errors? The only reason would be to do proper stack unwinding, and if the proper response to an unrecoverable error is to terminate the process, I don't see the point of cleanup.
you most certainly never had to wrestle with operating systems keeping sockets open for a few minutes after a process was killed then. pleaaaase do unwind properly and release as much resources as you can.
In modern C++ this is already the case. For example, every container that implements an at() function will throw an exception if the (unsigned) index is >= size.
If you look at the assembly of a simple function only accessing a std:array using the [] operator, you will see that it's quite small. If you access it using at() you might see that the core function remains the same, except it now has a bounds-checking test which jumps to a huge postamble. The postamble will throw the exception. Since the CPU likes to execute instructions in sequence, we say that the happy path (which is indexing correctly) is now the fast path.