Subject: Re: [boost] Looking for thoughts on a new smart pointer: shared_ptr_nonnull
From: Gavin Lambert (gavinl_at_[hidden])
Date: 2013-10-07 18:32:16
On 10/7/2013 11:47 PM, Quoth Julian Gonggrijp:
> While I'm not Matt Calabrese, I think I can restate why it should be a
> precondition *especially* on the constructor. The entire point of the
> proposed non-null pointers is to guarantee that the pointer is not
> null, so that receiving functions and storing objects can assume that
> it is not null. If you zero-initialize a pointer whose very purpose is
> not to be null, you defeat the purpose of its existence.
This sounds to me like an argument *for* the position I am suggesting,
not against it.
Throwing an exception means that the internal bare pointer is never
initialised to null (or at least that if it is initialised to null, it
can never be accessed as such because the resulting object is not
retained), which means that it will never be accessed in an undefined way.
If you fail to throw the exception (and to assert, because you're in a
release build) then you *do* go on to set the bare pointer to null, and
*the program does not immediately fail*. By contrast, it will fail at
some unknown later point at which someone actually attempts to access
the pointer. If you're lucky, that's in the following line, and the bug
is easier to find. If you're unlucky, it's three hours later (because
the pointer was created as a member field in another object that is only
accessed rarely) and it's much harder to figure out what happened.
Checking in the constructor is instant and failsafe. Not checking is
just leaving a time bomb in the code.
> Note how this is entirely analogous to std::sort, whose purpose is to
> perform an operation on a valid range. If you pass an invalid range,
> you defeat the purpose of the existence of the algorithm. This is why
> such algorithms have the precondition that you pass a valid range,
> IOW, that you only use them for what they were meant to be used for.
The difference in that case is that there is no practical way to test
for an invalid range, so you have no choice but to rely on the caller
getting it right. (At least not without help from the iterators
themselves, which is sometimes supported but implementation-specific and
thus not reliable.)
Verifying that a supposedly-not-null bare pointer is in fact not null is
a trivial check, and one that it seems worthwhile to do prior to storing
that pointer in an object that wants to guarantee that its stored
pointer is not null.
> The simplest way to do that is by initializing all non-null pointers
> with make_shared or another non-null pointer.
make_shared can fail and throw an exception. So the user already has to
be prepared to deal with that concept anyway.
And while I don't think boost's implementation supports it, it's not
unreasonable to think that some STL's implementation of std::make_shared
could return an empty pointer if the user has configured "new" to return
null instead of throwing an exception on failure, which would then push
the responsibility of throwing the exception onto the not_null_ptr's
constructor, or of needlessly complicating every single construction
site to avoid UB.
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk