Reusing a Moved Container

Reusing a moved container?

From section 17.3.26 of the spec "valid but unspecified state":

an object state that is not specified except that the object’s invariants are met and operations on the object behave as specified for its type [ Example: If an object x of type std::vector<int> is in a valid but unspecified state, x.empty() can be
called unconditionally, and x.front() can be called only if x.empty() returns false. —end example ]

Therefore, the object is live. You can perform any operation that does not require a precondition (unless you verify the precondition first).

clear, for example, has no preconditions. And it will return the object to a known state. So just clear it and use it as normal.

Is it safe to reuse a std container after std::move?

After moving from the standard C++ library classes, they are in an unspecified state but otherwise entirely usable. The relevant section is 17.6.5.15 [lib.types.movedfrom]:

Objects of types defined in the C++ standard library may be moved from (12.8). Move operations may be explicitly specified or implicitly generated. Unless otherwise specified, such moved-from objects shall be
placed in a valid but unspecified state.

None of the containers specifies otherwise (as far as I know).

From the sounds of it, you are actually moving the elements, though. However, for these apply rather similar restrictions based on the definition of what MoveConstructible and MoveAssignable means (from section 17.6.3.1 [utility.arg.requirements]; rv is the argument to either move constructino or move assignment):

rv’s state is unspecified [ Note:rv must still meet the requirements of the library component that is using it. The operations listed in those requirements must work as specified whether rv has been moved from or not. —end note ]

Reuse variable after move

Your code is correct, but the intention that you are communicating is wrong.

If someone sees the declaration of function test he will think that when calling the function he is giving away the ownership of the variable that he is passing, because the variable will be moved.

In general, after a call to std::move(v) you should not reuse v.

What you should do in this case is declare test as void test(std::vector<int>& v) and you should call it just with test(v).
In this way it is clear that test will modify v, but you will be able to use it later.

Can I resize a vector that was moved from?

My question is: can I resize a moved-from std::vector, perform some operation on it, then resize it back to zero?

A moved from object should be in unspecified but valid state.

So you have the right to resize it (no precondition required for that). safe_zero it, and clear it.

(I did not understand what "pre-conditions" really are).

There are state conditions that object should have to not invoke UB.

For example, operator[](std::size_t i) requires that i < size().

resize(), clear() doesn't have requirements.

Can you reuse a moved std::string?

With a few exceptions (smart pointers, for instance), moved-from objects are left in a valid but unspecified state.

In a std::string that uses the small string optimization, for instance, if the string is small, there is no dynamic allocation, and a move is a copy. In that case it is perfectly valid for the implementation to leave the source string untouched, and not incur the extra cost of emptying the string.

Can I re-use a complex class like std::vector after std::move?

Your code always copy the Events vector, so Events = std::vector(); will simply erase the copied-from elements.

Why does it copy and not a move? Let's take a look at the Step constructor:

//                         that's a copy -----v
Step(std::vector<Event>&& InEvents) : Events {InEvents} {}

Indeed, the expression (InEvents) is an lvalue, because it has a name. You must cast it to an rvalue using std::move:

Step(std::vector<Event>&& InEvents) : Events {std::move(InEvents)} {}

When taking parameters by &&, remember that it's a maybe move since its simply a reference like any other, and you must move it explicitly.


This line won't compile:

Events = std::vector();

Maybe you meant this:

Events = {};

This will indeed allow you to reuse your vector. It will be reset in a way that leads to a determinate state.

I want the line Events = std::vector(); to cause EventAccumulator.Events to be reset to an empty vector, but I do NOT want NewStep.Events to be reset.

My question is, will this code do what I want?

C++ has value semantics. Unless NewStep contains a std::vector<Event>& you cannot affect a variable somewhere else. Also you move construct the vector in NewStep. That should tell you something: You constructed a new vector. No matter how you mutate the old vector, it cannot affect a distinct vector. This code will do what you want, if you correct the Step constructor.


Keep in mind that if you want to avoid many allocation, you will have to call reserve like this:

Events.reserve(60);

As I added in the comments, constructor are a special case for move semantics: taking by value and move it adds very little costs and most likely elided. Here's what I meant by that:

Step(std::vector<Event> InEvents) : Events {std::move(InEvents)} {}

If you pass by copy, then it will copy into InEvents and move it. If you pass by move, it calls the move constructor two times, no copy.

Since the cost of calling the move constructor is negligible, it saves you from writing an overload.

It only works in constructors since we cannot reuse capacity anyway. This is not true for the assignment or a setter function.

c++ reassign after moved

This is guaranteed. After the move, the vector is guaranteed to be empty, i.e. has no elements. It's fine to be assigned by other contents.

After the move, other is guaranteed to be empty().

Move assignment on containers: state of previously contained objects

In [container.requirements.general], we see

All existing elements of a are either move assigned to or destroyed.
Ensures: a shall be equal to the value that rv had before this
assignment.

Where a is the destination and rv is an rvalue. This could be achieved by swapping1 with elements of the source destination, but most likely is done by resizing then moving.

  1. It would have to be through a non-specialisable __swap, to ensure the move assignments do occur.

Reusing a moved container?

From section 17.3.26 of the spec "valid but unspecified state":

an object state that is not specified except that the object’s invariants are met and operations on the object behave as specified for its type [ Example: If an object x of type std::vector<int> is in a valid but unspecified state, x.empty() can be
called unconditionally, and x.front() can be called only if x.empty() returns false. —end example ]

Therefore, the object is live. You can perform any operation that does not require a precondition (unless you verify the precondition first).

clear, for example, has no preconditions. And it will return the object to a known state. So just clear it and use it as normal.



Related Topics



Leave a reply



Submit