You have to report some kind of failure. You can do this with exceptions or status codes. Personally, I lean toward status codes if possible, especially in library code.
There was once a time that you couldn't be sure that an exception would actually be caught unless the code doing the catching was built by the same version of the same compiler with the same flags as the code throwing the exception. That seems to have been largely solved on platforms that have a de facto standard ABI. But once I got used to reporting failures in library code without exceptions I found that there are valid reasons to continue to do so.
For one, libraries are meant to be used by third parties, and those parties may not be all too excited about writing exception safe code.
Exception safe code isn't only about resource management. Consider:
...
// m is a mutex
boost::scoped_lock(m); // now I can't forget to release the mutex
withdraw_money(acct1, 1000);
function_that_may_throw_exception();
deposit_money(acct2, 1000);
Of course it's possible to rewrite this:
// using Boost SCOPE_EXIT
bool commit = false;
boost::scoped_lock(m);
withdraw_money(acct1, 1000);
SCOPE_EXIT((&commit) (&acct1))
{
if (!commit) {
deposit_money(acct1, 1000);
}
}
function_that_may_throw_exception();
deposit_money(acct2, 1000);
commit = true;
Exceptions solve many problems, but they don't solve all problems associated with error handling/detection. Writing invalid code is just as possible with exceptions as it is with status codes.