Live data from Hacker News

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

0xghost.dev

171–180 of 220 posts

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

#171

Earlier quoted context omitted.

Systems programming in the large is hard, owning the category for decades harder still. Even languages that have tried to fast-follow and disrupt C++ end up looking a lot like C++. There is an irreducible complexity.

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…

> 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

It's not about irreducible complexity in systems programming, it's about irreducible complexity in the creation of higher level abstractions.

You could certainly implement something functionally equivalent to std::vector in C. What you couldn't do in C is implement std::vector correctly and efficiently for any type T. That's where much of the complexity comes from.

The hard part is giving the compiler enough information so that it can automate a lot of what would have to be manually written in a language like C, and to produce a result that is both correct and efficient.

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

#172

> 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 it…

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

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

#173
post #74

Earlier quoted context omitted.

I understand the individual rationales of C++ things but I lost the faith on the whole thing.

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 live with, and don't give in to the temptation of cherry-picking anything from a future standard. For instance, the decision of switching from C++14 to C++17 should be a major debate full of fistfighting.

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

#174

Before move semantics the HeavyObject problem was solved in most cases by specializing std::swap for each container. The design lesson I draw from this is that pursing a 100% general solution to a real problem is often worse than accepting a crude solution which covers the most important cases.

[deleted]

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

#175

Before move semantics the HeavyObject problem was solved in most cases by specializing std::swap for each container. The design lesson I draw from this is that pursing a 100% general solution to a real problem is often worse than accepting a crude solution which covers the most important cases.

That still leaves the problem of when to use std::swap vs ordinary assignment in generic (i.e. templated) code.

Like when std::vector needs to resize its underlying storage (as a result of push_back, for example), it has to decide which approach to use to copy/move items from the old storage to the new storage.

For std::vector, std::swap would probably be at least ok if not optimal, but for std::vector it would be overkill and therefore decidedly non-optimal. In the latter case, you want to do memcpy(new, old) and be done, not std::swap(old[i], new[i]) for each int.

I think a lot of the motive for adding move semantics to c++ has to do with giving the compiler enough information to produce results that are both optimal and correct in generic code.

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

#176
post #120

You read things like this and, first, you're reminded of Sideshow Bob [1] and it puts Rust concepts in context, namely: 1. Move semantics are to handle ownership. Ownership is a first-class concept in Rust. This is why; 2. C++ smart pointers (eg std::unique_ptr ) are likewise to handle ownership and incur a runtime cost where in Rust they are handled by the compiler with no runtime cost. Yes you can "cheat" (eg std::…

> C++ smart pointers (eg std::unique_ptr) are likewise to handle ownership and incur a runtime cost where in Rust they are handled by the compiler with no runtime cost.

What additional runtime cost is incurred by the use of std::unique_ptr? Either compared to Rust or compared to doing manual memory management in c++?

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

#177

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.

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.

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

#178
post #2

I always understood move as moving ownership, so it's not a misnomer. > std::move is like putting a sign on your object “I’m done with this, you can take its stuff.” Which exactly is moving ownership.

std::allow_move probably would have been a more accurate name for std::move.

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

#179
post #22

> So the standard library plays it safe: if your move constructor might throw (because you didn’t mark it noexcept), containers just copy everything instead. That “optimization” you thought you were getting? It’s not happening. This is a bit of a footgun and clang-tidy has a check for it: performance-noexcept-move-constructor. However, I don't think it's enabled by default!

Nothing about clang-tidy is enabled by default, and getting it to run at all in realistic projects is quite a chore.

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

#180

Earlier quoted context omitted.

> This is like trying to defend that you can't statically know the result of 1 + 2 It is completely unlike that. tsimionescu is asserting that they can always know statically whether `foo` will move its parameter. The function I provided is a counter-example to that assertion. Of course the branch body always moves, that's what it's there for. That has no bearing on the argument.

>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 constructor is not the subject of the debate.

You can literally take the function I posted, build a bit of scaffolding around it, and observe that whether the parameter is moved into `foo` or not is runtime behaviour: https://godbolt.org/z/jrPKhP35s

Post reply on HN