I have found out that C++ standard functions show very different behavior when having an exception. This seem to contradict its touting of “try/throw/catch”. Can anyone please briefly explain what are the C++ designer’s reasoning behind these choices?
-
Do nothing, for example, try to pop() a stack when it is empty (instead of throw a range_error), do sqrt(-1) (instead of throw a domain_error)
-
Return a zero pointer: for example, when doing illegal pointer downcasting (interesting, doing an illegal reference downcasting will throw a bad_cast)
-
Throw an exception, but this appear to a minority of functions, for example, substr()
-
Give user a choice of whether to throw an exception, for example, new() will throw bad_alloc() when out of memory, but you can also choose (nothrow) as an option of new().
Most of the behaviour of C++ library functions can be explained by the general C++ philosophy “you don’t pay for what you don’t use”. That means that any particular construction shouldn’t incur any unneeded overhead when you use it correctly.
Optionally, more expensive, checked versions may exist, such as
std::vector::at(), but it is up to you whether or not to use them.The example of
stack::pop()andsqrt()shows this philosophy in action: In order to throw an exception on error, you would always have to check whether the call is valid. This check is unnecessary if you already know that your call will succeed, so there is no mandatory check built into those functions. If you want a check, you can write one yourself.The default
newis slightly different, as it incorporates facilities for calling anew_handler, and so the checking is done anyway. (Recall that an exception is only expensive if you actually throw it, so that aspect isn’t so important.) If you wanted to, you could always replace your own globaloperator new()by one which literally just forwards the argument tomalloc(). (That would of course make it unsafe to use the defaultnewexpression, as you have no way of checking now that you can construct an object at the returned pointer. So you’ll end up writing a check yourself and using placement-new, which is almost exactly what the nothrow-version does.)