On 12/02/2016 11:57, Emil Dotchevski wrote:
It appears that you think that C++ exceptions are "unusual" in the same way OS exceptions are unusual: the OS detected something bad going on and raises an exception, as many OSes do for example in the case of dereferencing a null pointer. That's not at all what C++ exceptions are. They are in fact specifically designed to replace the need to return error codes, so that handling errors is simpler, safer and more testable.
Perhaps I'm biased by mostly developing on Windows+MSVC, but since that implements C++ exceptions by raising OS exceptions, and OS exceptions can be caught and processed just like C++ exceptions, I don't see any distinction between the two. (Though I'm aware that the OS exception handling mechanism is much more broken on non-Windows.) And yes, I think that exceptions should be reserved for unexpected cases. If a method has a case where it is expected to sometimes not produce a value, then it should use optional<T> or similar.
As for shared_ptr::operator*, I know it could be made to throw, but that would be incorrect design. I mentioned shared_ptr to make the point that you're wrong that this kind of design decision can not be made in generic C++ code. STL too is full of generic functions that throw to indicate a failure, and others that do not, without giving the user a choice. Even at the language level, consider that in C++ constructors don't give you the option to return an error code, the only way for them to fail is by throwing. Why? Because that is the correct design.
The only reason that shared_ptr::operator* does not throw is that the class author decided that this is likely a hot path and the calling code has *probably* already checked for null, so it is more *efficient* to omit the check entirely (and cause undefined behavior if called in violation of that assumption). As such an assert is used to *hopefully* catch those violations, but there are only limited cases in which it will do so. Checking and throwing will always be more *safe/correct*, but for this particular method the author chose to prioritise efficiency over safety. (Although shared_ptr has an additional bias -- if the operator* precondition is violated then even if the assert is omitted it's almost certainly going to immediately cause an OS fault anyway due to a null (or nearly-null) pointer access -- which is arguably equivalent to always-throw behaviour. Methods on other classes typically won't get that for free.) Don't get me wrong, I'm not saying that this is a bad thing. But it can also get applications into trouble and it is useful to provide both options. Just look at all the discussion recently about safe numeric libraries -- that's another case where the language has chosen to value efficiency over safety, and some applications wish for the reverse.