On 03-10-2013 17:04, Matt Calabrese wrote:
On Thu, Oct 3, 2013 at 7:23 AM, Thorsten Ottosen < thorsten.ottosen@dezide.com> wrote:
If I see a class called non_null_ptr, I pretty much expect it not to be null (it's an /invariant/), and use it assuming this.
Yes, of course, that's the whole point. Using an assert does not change that. Again, you assert preconditions. When you define preconditions for a function, it's the caller's responsibility to meet those preconditions, which is why you assert them rather than handle them (sometimes it's not even possible to really check the precondition).
How many other classes allow you to construct an illegal object (an object where the invariant is broken?). I can't give any example from Boost or the standard library.
Whenever you violate any precondition of any function you are potentially putting your program in an unreliable state, including the breaking of invariants of that object or others (this is true of Boost and the STL and other libraries).
I can't create a broken std::vector by means of the constructor. At least I don't know how to. WhenI think about it, std::string can crash if you pass it null_ptr. Has anybody profitted from that? It leads to subtle run-time bugs, and I ran into that a few months back.
It's pretty easy to come up with examples -- again, any function with specified preconditions can do potentially this.
It's not just any function. It's the constructor.
We are not making up these rules here. This is sort of the point of preconditions. For a more detailed explanation, you can reference Alexandrescu's post at http://akrzemi1.wordpress.com/2013/01/04/preconditions-part-i/ under the heading: "What if a precondition is violated?"
Yes, I'm well aware of the different discussion on this matter. I've studied this subject more than most. Notice Andrei quotes my wg21 paper with Lawrence Crowl in the end.
The point is, even if you happen to throw in a particular implementation, if the precondition is such that the constructor cannot take a null pointer, then the throwing behavior is never to be relied upon by the user anyway. It's still just undefined behavior. Asserting accomplishes this, allows your function to be noexcept, and does not open the door to people [incorrectly] handling the exception (any handling of such an exception doesn't get you out of UB-land since you've broken the precondition).
You forget that some people prefer to provide a nice log message and warn the users in a nice way instead of crashing the program. Anyway, if I had to design this class, I would do the following: a) don't provide any public constructor that takes a pointer b) let all construction happen via make_non_null_shared( ... ) This should remove the need for validating anything, and is therefore the least error-prone and canonical solution (there is not even a need to assert anything). -Thorsten