We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Copy, Move, and the Rule of Zero step 5 of 5
What is left after you move from it
In a language with destructive moves, moving out of a variable ends it — the compiler refuses to let you touch it again. C++ does not do that. The destructor still has to run, so the object must still be there, and the language settles for a weaker promise:
A moved-from object is in a valid but unspecified state.
Valid means the two things a destructor needs: it can be destroyed, and it can be assigned to. Every other question — what it contains, how long it is, whether it is empty — has no answer you are entitled to.
std::vector<int> b = std::move(a);
a.size(); // legal to call, and you may not rely on the answer
a.clear(); // fine — no precondition
a = other; // fine — assignment is how you give it a value again
a[0]; // undefined behaviour — has a precondition you cannot check
The distinction is precondition, not danger. size() promises nothing about
its result but is safe to call. operator[] requires an element to exist,
and you have no way to know whether one does.
Where this actually bites
It is almost never std::move(a); use(a); on adjacent lines. It is:
-
a move inside an
if, and a read after theif - a member moved out in a loop body, and read on the next iteration
- a variable moved into a lambda, and read in the enclosing function
- a “just logging it” line added six months later by someone who did not look up
Three types, three different answers
Because implementations vary and the standard does not, the honest summary is short:
| Type | After being moved from |
|---|---|
std::unique_ptr |
Guaranteed null. The standard says so explicitly. |
std::vector, std::string |
Unspecified. Usually empty, and a short string often is not moved at all. |
std::optional |
Still engaged — has_value() stays true, and the contained value is moved-from. |
That optional row surprises people every time, and it is why “moved-from
means empty” is not a rule you can carry around.
The rule you can carry:
Do not read a moved-from object. Ask your question before the move, or assign it a fresh value first.
Your task
Batch drain(std::vector<Record>& records);
Each Record holds a std::unique_ptr<std::vector<int>> which may be null.
Move every non-null sample list out of records into Batch::collected, in
order, and set Batch::with_samples to how many records had one.
The starter moves first and asks afterwards. unique_ptr is the one row of
that table where the answer is guaranteed, so the count comes back zero every
time — a bug that is fully deterministic here and would be maddening with a
std::vector member instead.
Move the question, not the move.
Stuck?
C++ reference solution
Sign in to attempt this problem and reveal the reference solution.