Does Moving Leave the Object in a Usable State

Does moving leave the object in a usable state?

From n3290, 17.6.5.15 Moved-from state of library types [lib.types.movedfrom]

  1. 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.

Since the state is valid, this means you can safely operate on v2 (e.g. by assigning to it, which would put it back to a known state). Since it is unspecified however, it means you cannot for instance rely on any particular value for v2.empty() as long as it is in this state (but calling it won't crash the program).

Note that this axiom of move semantics ("Moved from objects are left in a valid but unspecified state") is something that all code should strive towards (most of the time), not just the Standard Library components. Much like the semantics of copy constructors should be making a copy, but are not enforced to.

What happens to the moved-from object after std::move() is called?

It depends on how the result of std::move() is used. move itself does not do anything to the object.

How to handle invalid state after move especially for objects with validating constructor?

You are trying to maintain two invariants at once, and their semantics are in conflict. The first invariant is the validity of the certificate. The second is for memory management.

For the first invariant, you decided that there can be no invalid constructed object, but for the second, you decided that the object can be either valid or unspecified. This is only possible because the deallocation has a check somewhere.

There is no way around this: you either add a check for the first or you decouple the invariants. One way of decoupling them is to follow the design of std::lock_guard

cert c = open_cert(); // c is guaranteed to not have memory leaks and is movable
{
cert_guard cg{c}; // cg is guaranteed to be valid, but cg is non-movable
}

But wait, you might ask, how do you transfer the validity to another cert_guard?

Well, you can't.

That is the semantics you chose for the first invariant: it is valid exactly during the lifetime of the object. That is the entire point.

† Unspecified and invalid as far as the certificate is concerned.

If I move a local object into a function, will it still be valid afterward?

The code is valid, because there no actual moving is performed. Here is how you could make it invalid:

string f(std::string&& s) {
std::string res(std::move(s));
res += " plus extra";
return res;
}

The state of str after this call would be valid, but unspecified. This means that you could still assign a new value to str to put it back into a valid state, but you wouldn't be able to output it without invoking unspecified behavior (demo). See this Q&A for specifics on the moved-from state.

Is moving twice in a single full expression allowed

Is it legal C++ to use this function as follows?

Yes, that's fine.

If I remember correctly, there is a C/C++ rule that forbids modifying an object more than once in a single full expression.

Not quite. You can't modify an object more than once (or modify it and use its value) with unsequenced accesses.

Does this rule apply here?

No. Evaluating the function argument is sequenced before the function call, which is sequenced before the assignment. So the two accesses are sequenced, and all is good.

std::move on a stack object

Yes it's still valid, the innerscope object loses ownership of the content it previously had, and outerscope becomes the owner. std::move is like a vector swap. If you swap outer and inner, destroying inner won't affect the content now owned by outer.

How can moved objects be used?

Yes, you can move another object into it. std::swap does this.

C++ use after move: Valid use-case?

The standard guarantees that a moved-from unique_ptr does compare equal to nullptr. N4659 [unique.ptr]/4:

Additionally, u can, upon request, transfer ownership to another unique pointer u2. Upon completion of such a transfer, the following postconditions hold:

  • (4.1) u2.p is equal to the pre-transfer u.p,
  • (4.2) u.p is equal to nullptr, and
  • (4.3) if the pre-transfer u.d maintained state, such state has been transferred to u2.d.

These guarantees also imply that it's safe to move from one that's already been moved from.



Related Topics



Leave a reply



Submit