Live data from Hacker News

Move, simply

herbsutter.com

21–30 of 96 posts

Re: Move, simply

#21

Earlier quoted context omitted.

Don't you have to destroy the target object, before you move into it?

In this example no because of pointer. Where is the allocation for int* data located? It's not inside the object. You can think easily of the state of 'a' after calling move_into. But maybe with int data[32] as a data member this is harder to reason about. This is a way to illustrate move semantics and confusion behind what happens to the 'moved from' object.

You made the moved-to pointer point to the moved-from allocated heap data. Now nothing points to the heap data that was initially allocated by the moved-to object. So if you don't delete[] the moved-to data pointer, then you leaked 32 bytes of heap.

Re: Move, simply

#22
post #7

Earlier quoted context omitted.

>C++ moves are not destructive, as they are in Rust. This means that types must arrange for a "moved-from" state Using an object that has been moved from is only required not to lead to a crash - the object needs to be in a "valid but unspecified state". If there's no sentinel value, you can leave the object in pretty much any state at all, and it's on the caller to not do anything silly before resetting it. There's…

Herb Sutter's article shows why this is actually a big problem in practice. He has an example of a class containing a guaranteed-non-null owning pointer. Leaving an object of this class in a "valid but unspecified state" means when you move out of it, you'll have to replace the internal pointer with a pointer to some new allocation, the very thing move semantics is supposed to help avoid. He suggests that such a clas…

Not quite. Let's suppose that I've got a pointer to a rather large array, and that array can't be null. When I move, the moved-from then has to allocate a new array, to satisfy the can't-be-null constraint. So I have the overhead of the allocation. But I don't have the overhead of copying all the bytes of the array.

Re: Move, simply

#23

Earlier quoted context omitted.

In this example no because of pointer. Where is the allocation for int* data located? It's not inside the object. You can think easily of the state of 'a' after calling move_into. But maybe with int data[32] as a data member this is harder to reason about. This is a way to illustrate move semantics and confusion behind what happens to the 'moved from' object.

You made the moved-to pointer point to the moved-from allocated heap data. Now nothing points to the heap data that was initially allocated by the moved-to object. So if you don't delete[] the moved-to data pointer, then you leaked 32 bytes of heap.

What happens when the program exits?

Re: Move, simply

#24

I found it's much easier to understand the concepts behind move operations if you write an object that implemented move semantics by itself. class Object { int* data = new int[32]; void move_into( Object& object ) { object.data = data; data = nullptr; } ~Object() { delete[] data; } } Object a; Object b; a.move_into( b ); I think much of the confusion arises from wondering where is the allocation for int* data located…

Why would you alter data (set it to nullptr) in a destructor?

Re: Move, simply

#25

Earlier quoted context omitted.

You made the moved-to pointer point to the moved-from allocated heap data. Now nothing points to the heap data that was initially allocated by the moved-to object. So if you don't delete[] the moved-to data pointer, then you leaked 32 bytes of heap.

What happens when the program exits?

Then it doesn't matter. It matters if you run out of heap before the program exits, though.

Re: Move, simply

#26
post #24

I found it's much easier to understand the concepts behind move operations if you write an object that implemented move semantics by itself. class Object { int* data = new int[32]; void move_into( Object& object ) { object.data = data; data = nullptr; } ~Object() { delete[] data; } } Object a; Object b; a.move_into( b ); I think much of the confusion arises from wondering where is the allocation for int* data located…

Why would you alter data (set it to nullptr) in a destructor?

From my copy paste example. I fixed thank you.

Re: Move, simply

#27

Move semantics are a great addition to C++, but off the top of my head, there are two warts: - C++ moves are not destructive (as compared to move semantics in Rust). This means that types must arrange for a valid "moved-from" state. This isn't a huge issue if the type has a natural sentinel value, but not all types have one and I often end up wrapping class members in std::optional to arrange for one. Frankly, this i…

I also get moves confused with the return-value optimization, though my understanding is that RVO appeared earlier.

Re: Move, simply

#28
post #7

Earlier quoted context omitted.

Herb Sutter's article shows why this is actually a big problem in practice. He has an example of a class containing a guaranteed-non-null owning pointer. Leaving an object of this class in a "valid but unspecified state" means when you move out of it, you'll have to replace the internal pointer with a pointer to some new allocation, the very thing move semantics is supposed to help avoid. He suggests that such a clas…

Not quite. Let's suppose that I've got a pointer to a rather large array, and that array can't be null. When I move, the moved-from then has to allocate a new array, to satisfy the can't-be-null constraint. So I have the overhead of the allocation. But I don't have the overhead of copying all the bytes of the array.

That workaround is also problematic, because move constructors are supposed to be noexcept, but 'new' can throw std::bad_alloc. So you either have to lie to the compiler and tell it that your move constructor is noexcept (maybe reasonable in this case since a bad allocation will then call std::terminate), or else you have bad consequences like std::vector silently copying your object instead of moving it.

Re: Move, simply

#29
And this is about the point where I finally gave up on C++, I just wish I could get the time I spent learning all the rules and all their exceptions back.

It has now morphed into a language where today's optimal code looks horribly inefficient by yesterday's standards. Which adds another layer of uncertainty to what was already a pretty serious mess of a language.

Calling this simple is about as silly as it gets. Thanks, but no thanks, I have problems to solve.

Re: Move, simply

#30

I found it's much easier to understand the concepts behind move operations if you write an object that implemented move semantics by itself. class Object { int* data = new int[32]; void move_into( Object& object ) { object.data = data; data = nullptr; } ~Object() { delete[] data; } } Object a; Object b; a.move_into( b ); I think much of the confusion arises from wondering where is the allocation for int* data located…

I appreciate it's just an example, but it should be mentioned that this is a really bad class. If you write a custom destructor, you must also specify the move/copy constructors and move/assignment operators. Otherwise, the same data pointer will be deleted twice if the object is ever copied.
Post reply on HN