Is there a legal way to move from gsl::not_null<T>?

974 Views Asked by At

The Guidelines Support Library introduced not_null<T> who's purpose is to enforce an invariant on pointer-like types, advertently on smart pointers. However it's a known issue that not_null<unique_ptr<T>> doesn't work.

As far as I see the reason is that unique_ptr<T> is not copy-constructible and not_null<T> doesn't have a constructor that would move from its T. not_null<T> is not default-constructible either because it would break it's invariant. Even if we could construct not_null<unique_ptr<T>>, it would be impossible to meaningfully reach the unique_ptr inside because we couldn't copy unique_ptr and moving it would leave not_null<T> with a nullptr. It looks like a perfect trap.

I argued that we could legally move from a not_null<T> object in a specific context: just before it goes out of scope. In other words, moving from it should be the last access before destruction. That way the object with broken invariant wouldn't be observable to the rest of the program. (It would be observable for not_null's own code only.)

In the following examples let's assume that we can move from not_null<T>.

not_null<unique_ptr<int>> f()
{
    return make_unique<int>(1);
}

void g(not_null<unique_ptr<int>> p)
{
    ...
}

void h()
{
    auto p = f();
    g(make_unique<int>(2));
}
  1. Is my assumption correct that state of the not_null<unique_ptr<int>> returned from f() couldn't leak after moving from it (just for the example)?

  2. Is my assumption correct that state of the not_null<unique_ptr<int>> passed to g() couldn't leak after moving from it (just for the example)?

  3. Is it possible to allow this special kind of moving while prohibiting the common case of moving in C++14/17?

1

There are 1 best solutions below

1
On BEST ANSWER

1&2: Ignoring the fact that elision would render the question moot on any compiler worth using, yes. Also ignoring the fact that unique_ptr cannot "leak".

3: No.

This has been the subject of some debate on the ISO C++ proposals mailing list. The general concept is that of a "destructive move", where the act of moving from an object and destroying it are performed in the same call. But this would have to be a language feature; there is no way in C++14 to tell whether a move constructor/assignment is being called such that the given object is certainly about to be destroyed.