164

Does the standard define precisely what I can do with an object once it has been moved from? I used to think that all you can do with a moved-from object is do destruct it, but that would not be sufficient.

For example, take the function template swap as defined in the standard library:

template <typename T>
void swap(T& a, T& b)
{
    T c = std::move(a); // line 1
    a = std::move(b);   // line 2: assignment to moved-from object!
    b = std::move(c);   // line 3: assignment to moved-from object!
}

Obviously, it must be possible to assign to moved-from objects, otherwise lines 2 and 3 would fail. So what else can I do with moved-from objects? Where exactly can I find these details in the standard?

(By the way, why is it T c = std::move(a); instead of T c(std::move(a)); in line 1?)

fredoverflow
  • 256,549
  • 94
  • 388
  • 662

2 Answers2

141

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.

When an object is in an unspecified state, you can perform any operation on the object which has no preconditions. If there is an operation with preconditions you wish to perform, you can not directly perform that operation because you do not know if the unspecified-state of the object satisfies the preconditions.

Examples of operations that generally do not have preconditions:

  • destruction
  • assignment
  • const observers such as get, empty, size

Examples of operations that generally do have preconditions:

  • dereference
  • pop_back

This answer now appears in video format here: http://www.youtube.com/watch?v=vLinb2fgkHk&t=47m10s

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 1
    But I could simply check the preconditions just like with any other object, right? – fredoverflow Aug 11 '11 at 15:17
  • 6
    @FredOverflow As long as these checks themselves have no preconditions, of course. – Christian Rau Aug 11 '11 at 15:18
  • 2
    @Chris: But how is that different from a normal, not moved-from object? – fredoverflow Aug 11 '11 at 15:25
  • 1
    @FredOverflow Who said a moved-from object is different from a non-moved object. That is the point in all the answers (well, except 6502's), a moved-from object is still a completely valid and usable object. You just don't know in which (valid) state it is. Like said, moving doesn't destroy the object, but alter it. – Christian Rau Aug 11 '11 at 15:27
  • 1
    @FredOverflow: The only difference is that most operations leave an object in a specified state. The moved-from state is unspecified, but otherwise, not special. Of course if you are the author of your class, you are free to make your moved-from state as special as you want. But know that if you use your class in std algorithms, your type is required to meet the stated requirements of the std algorithm, whether in a moved-from state or not ([utility.arg.requirements], Tables 20 and 22). – Howard Hinnant Aug 11 '11 at 15:30
  • 2
    May-be should be a separate question, but does that mean: if I have a string with `char* buffer;` and `int length;` members, then my move constructor/assignment must swap (or set) the value of both? Or would it be OK, if the length was unspecified (meaning that `empty` and `size` return meaningless values)? – UncleBens Aug 11 '11 at 15:32
  • 1
    Amazing how the standard uses "shall be placed in a valid but unspecified state" when the code that will do this operation is possibly generated by the compiler. All of a sudden we got classes that were perfectly valid and idiomatic in C++ and that in C++0X are violating the standard because C++0X will automatically generate a broken move constructor. – 6502 Aug 11 '11 at 15:33
  • @UncleBens In this case your string object would be in an invalid state, I think, which should answer your question. Not all unspecified states are valid, but only the valid ones should be taken by moved-from objects. – Christian Rau Aug 11 '11 at 15:34
  • @Uncle: I think that warrants its own question. You have my upvote for sure ;) – fredoverflow Aug 11 '11 at 15:52
  • 1
    @6502: Will you please stop just ranting on and on? This is not the place. – Puppy Aug 11 '11 at 16:33
  • @UncleBens: In you class probably if there is a move constructor or move assignment it would be written by you. The compiler will not generate a move operation for a class that has an explicit destructor. – 6502 Aug 11 '11 at 19:21
  • 3
    @6502: You don't make sense. A C++03 class isn't "violating the C++0x standard" because a move ctor _if generated_ would violate the standard. And C++03 code wouldn't be moving that class so there's no reason for a move ctor to be generated. – MSalters Aug 12 '11 at 09:27
  • @MSalters: C++03 code can well put an object in an `std::vector` and call `std::sort` on that vector. The resulting code will possibly use move semantic including assignment to a moved-from object when compiled in C++0X mode. This by the way is not the only incompatible change... even just `std::vector v(n);` has a different semantic in C++0X. – 6502 Aug 12 '11 at 11:40
  • @UncleBens If empty or size (or any member function) might return meaningless values, the class would be effectively unusable. Also, if the buffer's storage was dynamically allocated by the class, you have to ensure that it is freed at some point, and that also applies to the buffer held by the target prior to the move. Furthermore, the source object's destructor must not apply free to an invalid address, or to the address it moved to the target. Swapping satisfies these rules, as, presumably, does freeing the destination's original buffer and setting the source to {0,nullptr}. – sdenham Mar 26 '17 at 14:38
  • Actually one should pay attention that the cite from standard is about "the C++ standard library" only. – Кое Кто Sep 29 '22 at 17:11
63

Moved-from objects exist in an unspecified, but valid, state. That suggests that whilst the object might not be capable of doing much anymore, all of its member functions should still exhibit defined behaviour — including operator= — and all its members in a defined state- and it still requires destruction. The Standard gives no specific definitions because it would be unique to each UDT, but you might be able to find specifications for Standard types. Some like containers are relatively obvious — they just move their contents around and an empty container is a well-defined valid state. Primitives don't modify the moved-from object.

Side note: I believe it's T c = std::move(a) so that if the move constructor (or copy constructor if no move is provided) is explicit the function will fail.

JDługosz
  • 5,592
  • 3
  • 24
  • 45
Puppy
  • 144,682
  • 38
  • 256
  • 465
  • 29
    Not *all* of its member functions will exhibit defined behavior. Only those with no preconditions. For example you probably don't want to `pop_back` a moved-from `vector`. But you can certainly find out if it is `empty()`. – Howard Hinnant Aug 11 '11 at 14:40
  • 7
    @Howard Hinnant: `pop_back` from an empty `vector` has undefined behaviour anyway, from memory, so I'm pretty sure that `pop_back` from a moved vector exhibiting undefined behaviour is consistent. – Puppy Aug 11 '11 at 14:50
  • 14
    We're discussing moved-from objects. Not objects known to be in an empty state. Moved-from objects have an unspecified state (unless of course otherwise specified). [lib.types.movedfrom] – Howard Hinnant Aug 11 '11 at 15:11
  • 6
    @Howard Unspecified, but valid, so `pop_back` still behaves like on any valid vector (may it even be an empty vector). – Christian Rau Aug 11 '11 at 15:15
  • I understand it as follows: the state of the object after moving can be in any valid state, and all the actions valid for the state can still be used. If the committee wanted they would have to go great lengths of specifying every possible state of object. – Red XIII Feb 01 '13 at 16:15
  • 1
    what do unspecified and valid mean in this context? – Ankur S Jun 19 '18 at 12:41
  • 1
    Defined behavior does not mean a defined result. So taking the example of a moved-from `vector`, one can find out if it is `empty()`, but one cannot expect that this returns a specified result, be it true or false. – Simon Sep 12 '19 at 14:17
  • @ChristianRau that's the point. It is not safe to `pop_back` a moved-from vector in the same way that it is not safe to `pop_back` an arbitrary vector (e.g. one you received as a parameter), because it might be empty or it might not be. – Caleth Jun 04 '21 at 14:53
  • @Simon I'd say the _only possible valid state_ for a moved-from vector is `empty()`. It has to survive its destructor and the move took away all allocated memory. – Spencer Jul 07 '22 at 18:26