Live data from Hacker News

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

0xghost.dev

141–150 of 220 posts

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

#141

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.

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

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

Throwing move is super weird too. I believe that it was a mistake to not treat user move like C++11 destructors and default to noexcept(true) on them. But it is what it is.

On the other hand, writing special member functions at all(move & copy constructor/assignment, destructor) is a smell for types that don't just manage the lifetime of an object(unique_ptr like things). People should not generally be writing them and being open to the mistake of getting noexcept wrong.

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

#143

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.

my take looking at languages beyond C++ is a very different one you want a well working general solution which works well (most of the time for most of the "generic code" (i.e. good defaults for the default use-case). and then add escape hatches for micro-optimizations, micro-control etc. C++ on the other hand was deeply rooted designed with micro optimizations and micro control first. "Generic solutions" where then…

C++ does have reasonable defaults. You never have to worry about move if you are using standard containers or unique_ptr.

But eventually those escape hatches come bite you and you need to worry about.

Complexity is inherent to the system. Wrapping it in a nice interface doesn’t make it go away.

—-

The problem I see is move semantics are a real thing in programming languages where types can own resources.

Most languages just choose not to handle them well or limit their feature set. For example swift tries to use copy on write to avoid it

So eventually feature creep happens and you get borrowing/move.

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

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

performance-noexcept-move-constructor is great but it also complains about move assignment operators, which are completely different beasts and are practically impossible to make noexcept if your destructors throw.

Throwing destructors will generally end in termination of the program if they are used as class members. Types like scope_exit are fine, but anywhere else will probably have noexcept(true) on it's destructor.

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

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

I never understood move semantics until I learned Rust. Everything is move by default and the compiler makes sure you never leave things in an unusable state.

This was a difficult mental hurdle to get over with Rust, but once you do, move semantics make a lot more sense.

edit: When I said everything is move by default, I mean everything that isn't "Copy", such as integers, floats, etc.

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

#146

Earlier quoted context omitted.

Can you show an example of what you mean? 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. Of course, `foo` can choose to further copy or move the data it receives, but it can't choose later on if it's copied or not. Now, if I give `foo` a pointer to myObj, it could of course choose to copy or move from it later and based…

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.

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

#147

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

Modern C++ is hard to get into for people who learned C++ in the 90s and then worked in other languages for a decade or two.

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

#148

Earlier quoted context omitted.

In that sense, std::move() is no different than other passing semantics. 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.

I'm not sure what you are saying. If we have foo(std::string a, std string b), and then call it like this: std::string x; std::string y; foo(std::move(x), y); Then x will be moved into a, and y will be copied into b. The callee has no say in this - it's just the compiler implementing the semantics of the language.

Who says there's only one resolution candidate? A different overload could be defined elsewhere that the compiler prefers for that particular combination of arguments, that doesn't cause a copy. std::move() works the same way. The semantics of the operation is defined not by what's at the call site, but by the context.

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

#149

> 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 thought "move doesn't move" was a fairly common C++ mantra at this point.

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

#150

Earlier quoted context omitted.

performance-noexcept-move-constructor is great but it also complains about move assignment operators, which are completely different beasts and are practically impossible to make noexcept if your destructors throw.

Throwing destructors will generally end in termination of the program if they are used as class members. Types like scope_exit are fine, but anywhere else will probably have noexcept(true) on it's destructor.

[deleted]
Post reply on HN