4

In this answer there is a sample implementation of move constructor of a string object:

    string(string&& that)   // string&& is an rvalue reference to a string
    {
        data = that.data;
        that.data = nullptr;
    }

And also beside this toy example I've seen many places that say

a moved object shall be in a valid but unspecified state.

For example a std::string is usually an empty one after being moved. My question is why bother ourself to change the state of a rvalue? If an rvalue refrence is a hint to the callee that "we no longer need the object, so do whatever you want with it", so why change it and set it to nullptr or make the string empty? We can do nothing with it if the caller has told us I no longer use it.

Amir reza Riahi
  • 1,540
  • 2
  • 8
  • 34
  • *"why bother ourself to change the state of a rvalue?"* Note that `that` is an lvalue not an rvalue. Also refer to dupe: [Why do we need to set rvalue reference to null in move constructor?](https://stackoverflow.com/questions/22114025/why-do-we-need-to-set-rvalue-reference-to-null-in-move-constructor) – Jason Aug 06 '22 at 17:00
  • 1
    @JasonLiam Where `that` is an lvalue? in the move constructor or outside of it? I think from the caller perspective `that` is an rvalue. – Amir reza Riahi Aug 06 '22 at 17:01
  • `string(string&& that) : data{std::exchange(that.data, nullptr)} { }` – Eljay Aug 06 '22 at 18:50
  • For most Standard C++ Library objects, a *moved object* is left in a **valid but unspecified state** — with exceptions, such as `std::unique_ptr` or `std::vector` have a well-defined post-moved-from state. For your own user-defined types, a moved object can be left in whatever state you deem fit for your application. (I tend to follow the "valid but unspecified state" pattern that the Standard C++ Library uses, because I'm a bear of very little brain and its easier for me to remember that way.) – Eljay Aug 06 '22 at 18:53

1 Answers1

3

Consider the destructor of the above class. When it's called with a data that's not nullptr after move, then the old object (the object you moved from) would destroy the buffer of the new object (the object you've moved to). Therefore, you need to somehow signal that the object destructor should not deallocate anything. One way is to erase the data.

There are other ways to signal it. A common pattern is to have a bool flag that's true until moved from (usually mutable bool, so moving from const is possible).

lorro
  • 10,687
  • 23
  • 36
  • Why should moving from const should be possible? – gerum Aug 06 '22 at 20:48
  • @gerum That’s mainly used when you’re generally immutable (i.e., everything is const). In this case, you normally copy quite much (at each update). When you move from const, that means that resources will belong to the new (also immutable) object. It also limits the lifetime of the old (const) object, which doesn’t change (beside the immutable flag that now stores that it’s moved from). In general, the old object is now ‘bound’ to the new object, i.e. you should not access it after the new one is destroyed (if ever). So it’s an optimization vs. copy. – lorro Aug 06 '22 at 20:54