Live data from Hacker News

C++ std::move doesn't move anything: A deep dive into Value Categories

0xghost.dev

191–200 of 220 posts

Re: C++ std::move doesn't move anything: A deep dive into Value Categories

#191

Earlier quoted context omitted.

I hear this a lot, but I don’t really understand how this manifests in language complexity like the stuff in TFA in practice. Like, I can understand how systems programming requiring programmers to think about questions like “how can I proceed if allocation fails? How does this code work in an embedded context with no heap?” is hard and irreducible. But I can’t understand why a language’s choice to impose complex rul…

> But I can’t understand why a language’s choice to impose complex rules like C++ move constructor hell is an inevitable outcome of irreducible complexity in systems programming. Programmer here for 30 years in C/C++. It is true that C++ has become a more complex language after rvalue references were introduced, but you have to understand the rationale behind C++: a language suitable for large scale systems programmi…

C++ doesn't have zero overhead, though. The committee is unwilling to take ABI breaks and so have left performance on the table. For instance, unique_ptr can't be passed in registers but T* can.

Zero overhead is a fiction the committee likes to tell themselves, but it's not true.

Re: C++ std::move doesn't move anything: A deep dive into Value Categories

#192

Earlier quoted context omitted.

aside from what others wrote, it’s also non local - whether std::move even does anything is dependent on the signature of foo - if foo takes it by const& you may think you’ve transferred ownership when it hasn’t actually happened.

That is static though, that `foo` takes its parameter by `const&` and will thus not move it is available to the compiler (or other tooling) at compile time. The point of contention is whether that is always the case, or whether there are situations where moving from the parameter is a runtime decision.

Others have already answered that for you. I specifically said it’s non local which means it’s difficult for a human to reason about at the call site.

Re: C++ std::move doesn't move anything: A deep dive into Value Categories

#193

Earlier quoted context omitted.

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.

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…

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.

Re: C++ std::move doesn't move anything: A deep dive into Value Categories

#194

Earlier quoted context omitted.

> 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.

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.

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?

Re: C++ std::move doesn't move anything: A deep dive into Value Categories

#195

Earlier quoted context omitted.

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.

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."

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.

Re: C++ std::move doesn't move anything: A deep dive into Value Categories

#196
post #64
post #60

> [std::move silently copies const values, because] If something is const, you can’t move from it by definition. Whoever wrote that definition should have a thing or two to learn from Rust. Different language I know, but it proves that it wasn't needed to cause so much confussion and collectively so much time and performance lost. Also, who writes rules like that and ends the day satisfied with the result? It seems u…

There was no Rust in 2011.

By 2011 Rust has a logo (basically its current logo), and it has a compiler written in Rust (a distant ancestor of today's main Rust compiler). It's approaching Rust 0.1 (released January 2012 apparently) which is a very different language from Rust 1.0 -- but that's a long way from "there was no Rust in 2011" to my mind.

Re: C++ std::move doesn't move anything: A deep dive into Value Categories

#197

Earlier quoted context omitted.

>Of course the branch body always moves >That has no bearing on the argument. That is the whole argument. Let me quote the other person: "My claim is that, if I call `foo(std::move(myObj))`, it is statically knowable if `foo` receives a copy of `myObj` or whether it is moved to it." It is saying that for "auto pp = std::move(p);" we will know if it uses the move assign constructor or the copy assign constructor.

> That is the whole argument No, it is not. > Let me quote the other person: "My claim is that, if I call `foo(std::move(myObj))`, it is statically knowable if `foo` receives a copy of `myObj` or whether it is moved to it." Yes. `foo`. > It is saying that for "auto pp = std::move(p);" we will know if it uses the move assign constructor or the copy assign constructor. `pp` is not `foo`. That `pp` uses a move construct…

Taking a step back I think the issue is that your foo takes an rvalue reference. Which is not the case we are talking about which is whether a move or copy constructor is used when constructing the parameter of foo.

Re: C++ std::move doesn't move anything: A deep dive into Value Categories

#198

Earlier quoted context omitted.

>Static analysis is about proving whether the code emitted by a compiler is actually called at runtime. That is but one thing that can static analysis can prove. It can also prove whether source code will call a move contractor or a copy constructor. Static analysis is about analyzing a program without actually running it. Analysizing what code is emitted is one way a program can be analyzed.

The call to a move cons/move assign does not happen at call time. When a function taking rvalue reference is called, it can still have two code paths, one that copies the argument, and one that moves it. All the && does is prevent lvalues from being passed as arguments. It's still just a reference, not a move. Indeed, in the callee it's an lvalue reference. But yeah, you can statically check if there exists a code pa…

What the callee does it out of scope. We are talking about a single assignment or construction of a variable. This has nothing to do with tracing execution. It happens at one place, and you can look at the place to see if it is using a copy or move contructor.

Re: C++ std::move doesn't move anything: A deep dive into Value Categories

#199

Earlier quoted context omitted.

No, it is not statically knowable if it is actually moved. void foo(Obj && arg) {} Does not move `arg`. It's fairly easy to write code that assumes `std::move` moves the value, but that can lead to bugs. For example: void some_function(std::vector &&); void some_function2(std::vector &&); void main() { std::vector a = { 1 }; some_function(std::move(a)); a.push_back(2); some_other_function(std::move(a)); } The expecta…

Is pushing to a moved-from vector even legal? I thought in general the only guarantee you have after a move is that is save to destruct the object.

The state of a moved-from value is valid but unspecified (note, not undefined). IIRC the spec says vector must be `empty()` after a move. So all implementations do the obvious thing and revert back to an empty vector.

Re: C++ std::move doesn't move anything: A deep dive into Value Categories

#200

Earlier quoted context omitted.

The way C++ has developed over the past 20 years seems similar to someone starting with an algorithm that fails for some edge cases, and patching the behavior with a different hack for each edge case, which breaks other cases, then patching those, and on and on forever.

I think the way to be successful with C++ is to 1. Pick a sensible subset of the language that you allow in your project, and ban everything else. How much that subset should include is a valid debate and reasonable people can disagree, but I don't know of any successful C++ project that just YOLOs every part of the language into the project. And 2. (related) Pick the earliest possible standard that your team can liv…

Things start to break apart when you have dependencies that adopt newer standards or use broader features. There is only so much you can do unless you would like to reimplement libraries like SKIA, doctest, Qt6 or any modern game engine. It gets worse with security and updates. At some point a library will require a newer standard otherwise you have to adopt the entire codebase and assume the entire responsibility of all security updates.

At that point you are slowly rewriting the universe. So you can also do it in Rust tbh (which provides seamless updates and integration between epochs/editions).

Post reply on HN