Comment by ghm2180

Comment by ghm2180 11 hours ago

19 replies

> Let me put this in simpler terms: std::move is like putting a sign on your object “I’m done with this, you can take its stuff.”

and later:

> Specifically, that ‘sign’ (the rvalue reference type) tells the compiler to select the Move Constructor instead of the Copy Constructor.

This is the best conceptual definition of what `std::move` is. I feel that is how every book should explain these concepts in C++ because its not a trivial language to get into for programmers who have worked with differently opiniated languages like python and java.

If you read Effective Modern C++ right Item 23 on this, it takes quite a bit to figure out what its really for.

dsnr 11 hours ago

In simpler terms

1. You must implement a move constructor or a move assignment operator in order for std::move to do anything

2. The moved object could be left in an unusable state, depending on your implementation, after stealing its internal resources.

  • bitbasher 10 hours ago

    I never understood move semantics until I learned Rust. Everything is move by default and the compiler makes sure you never leave things in an unusable state.

    This was a difficult mental hurdle to get over with Rust, but once you do, move semantics make a lot more sense.

    edit: When I said everything is move by default, I mean everything that isn't "Copy", such as integers, floats, etc.

    • Conscat 9 hours ago

      What Rust loses with that decision is the ability to program the "semantics" in move semantics. Rust has no distinction between hypothetical place constructor and value constructor.

      • anematode 9 hours ago

        A loss of functionality, but arguably a good thing, e.g. moving will never throw an exception/panic so you don't need an equivalent to is_nothrow_move_constructible

      • lowq 4 hours ago

        I sure don't miss the footguns and raw boilerplate that is having a copy constructor, move constructor, copy assignment operator, move assignment operator, and destructor, per class.

        Yes, you should avoid the manual memory management that necessitates writing them. But work with a team of developers fresh out of school and next thing you know your codebase will be brimming with this kind of busywork.

  • grogers 10 hours ago

    > You must implement a move constructor or a move assignment operator in order for std::move to do anything

    Bit of a nitpick, but there are sometimes other functions with overloads for rvalue references to move the contents out - think something like std::optional's `value() &&`. And you don't necessarily need to implement those move constructor/assignment functions yourself, typically the compiler generated functions are what you want (i.e. the rule of 5 or 0)

  • jjmarr 8 hours ago

    > The moved object could be left in an unusable state, depending on your implementation, after stealing its internal resources.

    The "proper" semantics are that it leaves the object in a valid but unspecified state. So, invariants still hold, you can call functions on it, or assign to it.

    • masklinn 8 hours ago

      > you can call functions on it

      Only functions with no preconditions, unless the type makes more guarantees as to the moved-from state.

      • jjmarr 6 hours ago

        The guarantees is that a moved-from state is in an otherwise valid state.

        So, you can do things like check if a moved from std::vector is empty (often the case in practice), then start appending elements to it.

yunnpp 10 hours ago

I thought "move doesn't move" was a fairly common C++ mantra at this point.

  • locknitpicker 9 hours ago

    > I thought "move doesn't move" was a fairly common C++ mantra at this point.

    It is. The fact that std::move is just a cast and that move constructors are expected to transfer resources are basic intro to C++ topics, covered in intro to constructors.

    • LexiMax 8 hours ago

      It's far too late to put the genie back in the bottle, but I am morbidly curious as to why the standards committee didn't choose an approach that made moves destructive.

      • jandrewrogers 6 hours ago

        It solves some rare edge cases where the destruction of the moved-from object must be deferred -- the memory is still live even if the object is semantically dead. Non-destructive moves separate those concerns.

        There is a related concept of "relocatable" objects in C++ where the move is semantically destructive but the destructor is never called for the moved-from object.

        C++ tries to accommodate a lot of rare cases that you really only see in low-level systems code. There are many features in C++ that seem fairly useless to most people (e.g. std::launder) but are indispensable when you come across the specific problem they were intended to solve.

        • LexiMax 3 hours ago

          As someone who has actually had to launder pointers before, I would characterize gremlins like std::launder as escape hatches to dig your way out of dilemmas specific to C++ that the language was responsible for burying you under in the first place.

      • tialaramex 3 hours ago

        https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n13...

        "There is significant desire among C++ programmers for what we call destructive move semantics [...]"

        "In the end, we simply gave up on this as too much pain for not enough gain."

        • LexiMax 3 hours ago

          groan

          > When dealing with class hierarchies, destructive move semantics becomes problematic. If you move the base first, then the source has a constructed derived part and a destructed base part. If you move the derived part first then the target has a constructed derived part and a not-yet-constructed base part. Neither option seems viable. Several solutions to this dilemma have been explored.

          Add this to my "C++ chose the wrong kind of polymorphism to make first-class" tally.

      • dataflow 3 hours ago

        What would you want to happen when an object that's on the stack is moved? Do you want its destructor to run, or not? If not, how exactly do you want that to no longer occur? And what do you want to happen if the stack object is moved in multiple places? How willing are you to pay a performance or UB penalty for these?

qbane 7 hours ago

I read Effective Modern C++ years ago and was confused exactly like what you describe.

QuercusMax 10 hours ago

Modern C++ is hard to get into for people who learned C++ in the 90s and then worked in other languages for a decade or two.