Live data from Hacker News

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

0xghost.dev

161–170 of 220 posts

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

#161
post #137

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

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.

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

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

#162
post #128
post #32

About 28 years ago, I figured out that I’m just not smart enough to use C++. There are so many foot guns and so much rampant complexity that I can’t keep it all straight. I crave simplicity and it always felt like C++ craved the opposite.

c++ 03 was a lot easier. For instance, if you want to avoid unnecessary copy operations when returning a string, just return it in variable that you pass by reference (eg. void doSomething(string& str);) likewise avoid the vector class making unnecessary copies, simply by creating the objects on the heap and use a vector of pointers instead of values. It's a bit more ugly, but it works, and you don't need to read a 2…

Agreed that c++03 was much simpler, but that doesn't change the fact that there are useful things that are possible in modern c++ that simply were not possible before.

Like if I have a vector, in c++03 when it resizes it must copy every string from the old storage to the new storage. For a vector of size N, that's up to N+1 allocations (allowing for the possibility that std::string uses the small string optimization).

Granted, std::string doesn't have to allocate when copied if it's a "copy on write" implementation. IIRC, there were some implementations that used that technique when c++03 was the latest, but I don't think there are any that still do, due to other problems with COW.

In modern c++, that same vector resizing operation requires exactly one allocation (for the new vector storage), because all the strings can be moved from the old storage to the new.

Yes, you could have a vector of pointers to std::string, but now you've got yet another allocation (and indirection on access) for every string. In practice that tradeoff almost never makes sense, unless perhaps the strings have shared ownership (e.g. vector>).

Ultimately, I think there's really no question that the vector resizing optimization described above is useful in certain scenarios. Having said that, I do agree that the associated complexity is annoying. Therefore, the real question is whether it's possible to have these benefits with less complexity, and I personally don't know the answer to that.

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

#163

Do I really need care about this? I really hoped that I can just not bother wrapping things in std::move and let the compiler figure it out? I.e. if I have ``` std::string a = "hi"; std::string b = "world"; return {a, b}; // std::pair ``` I always assumed the compiler figures out that it can move these things? If not, why not? My ide tells me I should move, surely the compiler has more context to figure that out?

I think there's a consequence difference between the IDE being sure enough that a std::move is warranted to issue a lint, versus the compiler being 100% provably certain that inserting a move won't cause any issues.

Sure, but by the sound of the article, the compiler won't do the right thing?

Effectively, I'm a c++ novice, should I ever sprinkle move (under the constraints of the article)? Or will the compiler figure it out correctly for me and I can write my code without caring about this.

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

#164
post #161
post #137

Earlier quoted context omitted.

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.

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

> you can call functions on it

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

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

#165
post #149

Earlier quoted context omitted.

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

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

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

#166

Earlier quoted context omitted.

>Sure overload resolution happens first, but once the compiler has found the correct match then the way arguments are passed depends only on the function signature of that match (callee), and how the caller is passing. Yes, and std::move() works exactly the same. The compiler first determines whether to move or to copy, and then generates the call to the corresponding constructor or assignment operator. Just like how…

Not sure how this relates to your original claim, below, that I have been responding to? > Just because you wrote at the call site that you want to pass a copy of your object doesn't mean that the callee will actually make a copy of it.

Meaning, just like move semantics, overload resolution can sometimes be surprising, and the compiler may not always do what you expected if you don't fully understand the types you're working with. std::move() is not special in this sense.

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

#167

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…

> but you have to understand the rationale behind C++: a language suitable for large scale systems programming with ZERO OVERHEAD.

Is this the reason why C++ was created, or the last remaining niche that C++ is holding onto?

I remember the early 90's, and it very much seemed like C++ was being pushed as both a general-purpose language and the logical successor to C, insert Linus Torvalds rant here. On top of that, C++ made the decision to privilege a form of polymorphism that had pointer-chasing baked into its internal design, as well as having a good chunk of the standard library being considered a footgun best to avoid due to how much it blew up compile-times.

I think that C++ is a zero-overhead language now because a series of general purpose languages that came afterwards took the other niches away from it, plus the benefit of 30+ years worth of compiler optimizations that were originally largely aimed at the mountain of C code that was out there.

EDIT: Almost forgot about exceptions, the other enormous performance footgun that was an early pre-standard C++ feature.

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

#168

Earlier quoted context omitted.

This is like trying to defend that you can't statically know the result of 1 + 2 because: void foo() { std::random_device rdev {}; auto dist = std::uniform_int_distribution (0, 1); if (dist(rdev)) { int res = 1 + 2; } } I can tell you for sure that the result of 1 + 2 will be 3.

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

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

#169
post #52

Earlier quoted context omitted.

I don't understand the downvoted here. Either the compiler emits the code to call a move constructor or it doesn't.

Static analysis is about proving whether the code emitted by a compiler is actually called at runtime. It's not simply about the presence of that code. Code can be emitted but never executed.

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

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

#170
post #73

[flagged]

Value categories actually just are confusing in a language as complicated as C++. I'm not willing to bet that even senior C++ developers are always going to be able to deduce the correct value category. And worse, in typical C++ fashion, there is still little guaranteed as far as when std::move will actually cause a move. The implementation is still given a lot of leeway. I've been surprised before and you basically…

> even senior C++ developers are always going to be able to deduce the correct value category

Depends what "senior" means in this context. Someone with 20-years of domain experience in utility billing, who happened to be writing C++ for those 20 years? Probably not.

Someone who has been studying and teaching C++ for 20 years? Yes they are able to tell you the value category at a glance.

Language experience is not something you develop accidentally, you don't slip into just because you're using the language. Such tacit experience quickly plateaus. If you make the language itself the object of study, you will quickly surpass "mere" practitioners.

This is true of most popular programming languages in my experience. I find very, very few Python programmers understand the language at an implementation level, can explain the iterator protocol or what `@coroutine` actually used to do, how `__slots__` works, etc.

C++ is not unique in this, although it is old and has had a lot more time to develop strange corners.

Post reply on HN