0

According to the standard, you can only do certain things with moved-from objects. However, what is technically considered a moved-from objects?

let's say I do this:

int x = 42;
std::move(x);
int y = x; // is this ok?

std::move is just a cast to a rvalue-reference. If this reference is not consumed, does that count as a move? Could I still use x in the example above, after the call to std::move?

Touloudou
  • 2,079
  • 1
  • 17
  • 28
  • 1
    related/dupe: https://stackoverflow.com/questions/3106110/what-is-move-semantics – NathanOliver Jun 04 '21 at 12:45
  • 7
    `std::move(x)` means `x` has permission to be moved from. It does not mean that `x` is necessarily moved, which in this code it is not moved. – Eljay Jun 04 '21 at 12:45
  • 1
    Consider `x = std::move(y);` The `std::move(y)` converts the LValue `y` to an RValue (if possible). The actual move happens in the assignment (if there is a move assignment) while the `std::move()` enables that move assignment is preferred over copy assignment. – Scheff's Cat Jun 04 '21 at 12:49
  • But let's imagine you define your own type, and don't do anything in the move assignment. Then what? is it still considered moved-from? – Touloudou Jun 04 '21 at 12:51
  • A move assignment is the (maybe overloaded) operator of the left hand side. In `std::move(x)` there is no assignment. It's just an RValue cast and `std::move(x);` will surely be recognized by the compiler as no-op. `std::move()` is prohibited to be overloaded. If you do it's Undefined Behavior. (Only, a few things of `std::` may be overloaded at all.) – Scheff's Cat Jun 04 '21 at 12:55
  • What happens with the right hand side depends on the implementation of the overloaded move assignment. It may leave the right hand side in a modified state or not. FYI: [C.64: A move operation should move and leave its source in a valid state](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c64-a-move-operation-should-move-and-leave-its-source-in-a-valid-state) – Scheff's Cat Jun 04 '21 at 12:58
  • 2
    Most standard C++ library objects, when moved from, are left in a *valid but unspecified state* — suitable only for being assigned to, or destructed. Some standard C++ library objects have additional guarantees, like a moved-from std::unique_ptr is left holding a nullptr, or a moved-from std::vector is left as an empty vector. Also, any method without any preconditions can be called. Your **own** user defined types can be left in whatever state you program them to be left in, which can be *valid but unspecified* or can be *valid and specified*. – Eljay Jun 04 '21 at 13:01
  • I think [this is dupe](https://stackoverflow.com/q/3413470/1387438). I'm not fully convince this should be closed as dupe. Will not vote it as dupe since I have "supper powers" and my vote would be a final resolution. So if someone agrees please cast close vote. – Marek R Jun 04 '21 at 13:35

1 Answers1

2

Well, std::move(x); is simply casting to r-value reference - it doesn't do anything. But OK, assume you had this:

int x = 42;
int z = std::move(x);
int y = x; // is this ok?

Yes, it is OK and x, y, z will all be 42. Because for trivial types moving is the same as copying.

But what of other more complex classes?

In general, there are only a few strict requirements for moving and move-from objects.

  1. moved-from object needs be copy/move assignable and destructible.
  2. target should be "equal" to pre-moved source.

These requirements are necessary. A lot of STL functionality and other libraries will not operate properly with types that fail to support these.

In practice, simple POD types simply perform a copy on move while containers transfer resource ownership from source to target while destroying whatever resources target owned. Technically, data swap between the objects is also a legitimate action - and some believe it to be a good idea - I strongly disagree with it because if I wanted to swap data then I'd trigger std::swap instead of std::move.

Nevertheless, there is no strict requirement what move must do to moved-from objects - which is important when dealing with complex classes. For instance, one can write a custom allocator for std::vector that will explicitly forbid to move, in which case on move some sort of data copying mixed with data moving will take place and different versions of STL may or may not clear original object.

Notes: cppreference has explicitly stated requirements on moved-from objects for majority of classes in the documentation. So you'd know what exactly what to expect and it's not always intuitive. Usually they go for the laziest action possible.

ALX23z
  • 4,456
  • 1
  • 11
  • 18