In my (unrequested) opinion, exceptions are useful when there are errors you don’t really want to handle during normal execution. Things like malloc failing, or open files suddenly disappearing out from under you (e.g. someone yanked a hard drive). They are errors but trying to handle every little bit of how a computer can go wrong is not really a feasible task, although you might be inclined to handle some.
I agree with your two examples (malloc failure and hard drive issue).
> They are errors but trying to handle every little bit of how a computer can go wrong is not really a feasible task, although you might be inclined to handle some.
Though, using error values, it's not that different.
1. check if the error is something that you care about
2. if not, check if it is something that you should pass back to the caller (in general that's an assert "if not null/empty"), if it is then return it
3. otherwise continue what you're doing in the current function
You have the same result without a need for exception, you only handle what you care about in the current scope, or let the chain of callers decide what they want to do with the error.
It's too easy to forget to check or propagate an error value (in most languages) though. Exceptions are nice because you can't forget - you either handle it somewhere in the call stack or the process is killed.
A particular task may not be recoverable, but the process state can be. Photoshop shouldn't abort just because you pulled the thumb drive you were reading/writing from. A concurrent network server with 9999 connected clients shouldn't abort because malloc failed initializing connection state for the 10000th.[1] This isn't just a QoI issue, it's also a security issue.
[1] Many types of network servers, for example a multimedia streaming server or a SOCKS proxy, only need to allocate dynamic memory during the early phase of the connection. After that the client can be served indefinitely. If you're writing a library, best practice is to assume your caller can handle malloc failure; if not they can choose to abort.
It depends on the context. Just because you can't continue writing to a file that was open, doesn't mean the program should crash.
For example, in a user-facing desktop application, maybe you just catch the exception in a high-level place, log the error, report it to the user, and explain that the action they just attempted failed. They can then try again, without having to restart the entire application.
Because only the caller of a function gets to decide if an error is recoverable or not. Also note that this reasoning applies to all kinds of error handling, not just exceptions.