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

0xghost.dev

247 points by signa11 4 days ago


ghm2180 - 2 days ago

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

krona - 2 days ago

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

drob518 - 2 days ago

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.

groundzeros2015 - 2 days ago

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.

Fiveplus - 2 days ago

Regarding mistake 1: return std::move(local_var), it is worth clarifying why this is technically a pessimization beyond just breaking NRVO. It comes down to the change in C++17 regarding prvalues.

> Pre-C++17, a prvalue was a temporary object.

> Post-C++17, a prvalue is an initializer. It has no identity and occupies no storage until it is materialized.

rurban - 2 days ago

Should have be called give(). But naming things correctly is hard, and the C++ committee is known to do a lot of things incorrectly

fooker - 2 days ago

Maybe std::make_movable would have been a slightly better name, but it's so much simpler to write std::move.

QuadmasterXLII - 2 days ago

C++ is the high rocky mountain pass between the fertile great plains of C and the weird but ultimately survivable California of Rust.

- 2 days ago
[deleted]
injidup - 2 days ago

You should almost never ever be writing your own move constructors. Use compiler generated defaults. It's only for very rare specialist classes that you need to override compiler generated defaults. Many times when you think you need to you often don't.

cenamus - 2 days ago

I found the previous discussion and article very helpful

https://news.ycombinator.com/item?id=45799157 (87 comments)

- 2 days ago
[deleted]
ohnoesjmr - 2 days ago

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?

shmerl - 2 days ago

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.