79

Is unique_ptr guaranteed to store nullptr after move?

std::unique_ptr<int> p1{new int{23}};
std::unique_ptr<int> p2{std::move(p1)};
assert(!p1); // is this always true?
Praetorian
  • 100,267
  • 15
  • 224
  • 307
lizarisk
  • 6,692
  • 9
  • 42
  • 66
  • 2
    _Technically_ the state in which `::move` leaves an element is unspecified. I don't think there is any guarantee on the smart pointer's end either. That said, I'll leave it to the CPP experts to answer :) – Benjamin Gruenbaum Jun 05 '14 at 13:42
  • 1
    Wait, never mind, yes. Release explicitly sets it to nullptr. This is guaranteed by the `unique_ptr` – Benjamin Gruenbaum Jun 05 '14 at 13:44
  • gcc 4.8.1 here, p1 is null after the std::move – Exceptyon Jun 05 '14 at 13:44
  • 1
    See any reference on [`std::unique_ptr::operator:`](http://en.cppreference.com/w/cpp/memory/unique_ptr/operator=) and [`std::unique_ptr::release`](http://en.cppreference.com/w/cpp/memory/unique_ptr/release). – rubenvb Jun 05 '14 at 13:52
  • just look at move semantics... – Deduplicator Jun 05 '14 at 13:52
  • @BenjaminGruenbaum: Uh, `std::move` doesn't leave elements in any particular state in any case. i.e. `(void)std::move(x)` will not modify `x` no matter what the type of `x` is. It's not `std::move` that does anything, it's the movement of the object itself (move assignment or move construction), which `std::move` *cannot* make any claims about (even claiming the behavior is unspecified is beyond its jurisdiction, so to speak). – user541686 Jun 05 '14 at 22:40
  • @Mehrdad right, which is why I said "I don't think there is any guarantee on the smart pointer" immediately after, which I immediately corrected to "oh wait, it explicitly sets it to nullptr". – Benjamin Gruenbaum Jun 05 '14 at 22:52

2 Answers2

71

Yes, you can compare it to nullptr after the move and it is guaranteed to compare equal.

From §20.8.1/4 [unique.ptr]

Additionally, u can, upon request, transfer ownership to another unique pointer u2. Upon completion of such a transfer, the following postconditions hold:
u2.p is equal to the pre-transfer u.p,
u.p is equal to nullptr, and
...

(the member p is described earlier as — a unique pointer is an object u that stores a pointer to a second object p)

Praetorian
  • 100,267
  • 15
  • 224
  • 307
  • This is clearly true after calling release(). But std::move doesn't call release(). So how does the compiler know to restore the invariant of the unique_ptr? – mabraham Jul 11 '18 at 23:20
  • 1
    @mabraham `p2{std::move(p1)}` is move constructing `p2` from `p1`. That's the *requested transfer of ownership* in the text I quoted above. The move constructor implementation will make sure all the postconditions are satisfied. – Praetorian Jul 12 '18 at 00:10
  • Thanks. Rephrasing: the use of the move constructor requests transfer of ownership, and that must maintain the invariant of p1. std::move just enables the use of the move constructor. – mabraham Jul 12 '18 at 00:18
10

Yes. From C++2011 Standard Section 20.7.1/4:

Additionally, u can, upon request, transfer ownership to another unique pointer u2. Upon completion of such a transfer, the following postconditions hold [...] [the source unique_ptr] is equal to nullptr...

Paul Evans
  • 26,111
  • 3
  • 30
  • 50