Closed BenFrantzDale closed 2 years ago
Currently this
not_null<std::unique_ptr<Child>> c = not_null(std::make_unique<Child>())
not_null<std::unique_ptr<Base>> b = std::move(c);
won't compile either, and I think that is a good safety net.
I believe it comes down to how safe shall the class be? How many cases of misuse shall it be able to catch and prevent?
Fair enough. I disagree, but I can see the argument for not_null
absolutely never being null (even though that limits its utility with std::unique_ptr
.
Even so, why not provide operator const T&() const
so at least
not_null<std::unique_ptr<Child>> c = not_null(std::make_unique<Child>());
const std::unique_ptr<Child>& b = c;
works? b = c.get()
works there but there's no implicit conversion.
Personally I've been swayed (mostly by Sean Parent, I think) toward the view that a moved-from object should be allowed to break its invariants in the sense that the only things that a typical moved-from object should have happen to it are destruction and assignment (and possibly other assignment-like things). Being std::semiregular
is nice too, but doesn't make sense for classes like not_null
that exist to impose conditions. If I had my druthers, not_null
would check its invariant in get()
(which it does) and allow implicit conversions where the contained pointer, including operator T&&() &&
so not_null<unique_ptr<T>> p = foo()
would work even without copy elision.
Editors call: not_null<unique_ptr<T>>
already doesn't support anything but looking at the object in place. Moving from it to a different object (even of the same type) does not work because then we can't guarantee the source is still "not null" -- in fact it must be null.
Thanks @hsutter. That makes it a non-starter for many of my important uses where I have code that needs to be passed a not_null<unique_ptr<T>>
I went with gsl-lite and it works beautifully. I think this is an important use case that needs to be addressed one way or another.
Related: I created not_null<unique_ptr<T>> nn_make_unique<T>(auto&&…)
and it’s very useful.
An rvalue
not_null<unique_ptr<Child>>
won't convert to anot_null<unique_ptr<Base>>
like it does with aunique_ptr
or anot_null<shared_ptr<>>
: https://godbolt.org/z/o7nGEde3sI'd expect the rvalue to move its
unique_ptr
out, leaving it in a moved-from state that, yes, is null, but likestlab::copy_on_write<T>
it would be in a state that nobody should be reading from.It won't allow conversion to raw
unique_ptr<Base>
either, likestd::unique_ptr<Base> b = not_null(std::make_unique<Child>());
.Related:
std::unique_ptr<int> p = not_null(std::make_unique<int>());
doesn't compile either. I think it needsalthough probably more to allow it to convert a
not_null<unique_ptr<Child>>&&
tounique_ptr<Base>
.