>If the API is that a function is infallible and then I decide that it’s a fallible function then that’s a pretty major change and I’m just gonna have to update all the call sites to deal with a fallible return result.
What if you don't control those call sites?
>Might as well just call std::abort.
Sure. I mean, not really, because the caller cannot handle an abort. You're making a decision for the caller that the situation is unresolvable, where the caller might disagree.
>No. You’ve simply made a new contract and the new contract is that under certain cases an error is returned in the form of an exception.
If the function doesn't use exceptions for normal error conditions, then no, it's not a new contract, because you don't need to do or know anything specific to handle the situation. You could do something like
void transaction(){
try{
//...
commit();
}catch (std::exception &){
rollback();
}
}
and not have to worry about the specifics. It's just an exception. You don't have to care about
what exactly happened, you just care
that something that couldn't be resolved happened. When exceptions are misused you see stuff like
try{
some_function();
}catch (SomeErrorConditionSpecificToSomeFunction &){
//...
}
Not always, but this does usually mean that the exception is part of the contract of the function. It's a condition that the caller
must handle as part of the normal usage of the function. FileNotFound exceptions are quite often a prime example of exception abuse.
Replying to your other comment here:
>They’re fallible functions. Don’t write fallible APIs that require exceptions to report errors! That’s bad API design!
I disagree. You should ensure your arguments are valid before indexing vectors and dereferencing optionals. You wouldn't iterate a vector like this, I imagine?
for (size_t i = 0;; i++){
auto x = vector.at(i);
if (!x.has_value())
break;
//...
}