Live data from Hacker News

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

0xghost.dev

201–210 of 220 posts

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

#201

Earlier quoted context omitted.

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

> Add this to my "C++ chose the wrong kind of polymorphism to make first-class" tally.

Is it really the "wrong kind of polymorphism" if it isn't causing any problem and it didn't prevented rolling out features such as semantic support for move constructors?

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

#202
post #117
post #115

Earlier quoted context omitted.

Rust does, Rust editions do not have a story for them.

The story is that it must not matter which edition a library was compiled with - it's the boundary layer at which different editions interoperate with eachother.

Provided everything is available in source code, there are no semantic changes on the boundary level, or standard library types being used on the library public API that changed across editions.

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

#203
post #202
post #117

Earlier quoted context omitted.

The story is that it must not matter which edition a library was compiled with - it's the boundary layer at which different editions interoperate with eachother.

Provided everything is available in source code, there are no semantic changes on the boundary level, or standard library types being used on the library public API that changed across editions.

No. Not provided any of that. Must not matter really does mean "must not matter" here.

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

#204
post #64

Earlier quoted context omitted.

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.

Technically correctly, when considering private circles, as per Wikipedia it was announced to the world in January 2012.

Hardly something someone at WG21 would have taken into consideration when writing papers for C++0x.

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

#205

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.

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?

> How willing are you to pay a performance or UB penalty for these?

OP wasn't even able to frame an actual problem. So why is this blend of vacuous criticism even entertained?

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

#206
post #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.

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

It's been a while since I read it, but if I recall correctly the book focused on special member functions and when the compiler actually stepped in for the developer, not the actual concept of move semantics. Those are different things.

Special member functions is a development experience issue, and covers aspects such as "can I get the compiler going to generate code for me". If you write code that tells the compiler it should not generate move constructors for you, often it ends up generating copy constructors. That's it.

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

#207
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 a…

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

I think you're missing a fair deal of insight into the issue.

The move semantics proposal documents this aspect in clear and unambiguous terms:

- Almost any class should be able to create a nothrow move assignment operator.

- a basic requirement is that a class must have a valid resource less state (i.e., remain in a valid state after having been moved)

- those that can't, shouldn't define move semantics.

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

#208

Earlier quoted context omitted.

What does processor but width have to do with the likelihood of allocation failures?

I think what he means is that on a 64-bit system you have a massive virtual address space (typically only 48-bit, but that's still 256TB), and since malloc allocates from virtual address space, not limited by physical memory, it is unlikely you will get a malloc failure (unless you are trying to allocate more than 256TB per process, maybe due to a memory leak).

> it is unlikely you will get a malloc failure

That assertion completely misses the point. The scenarios involving move constructors throwing exceptions involve objects being stuck in an inconsistent/zombie state. In fact, the whole point of a move constructors is to avoid having to allocate memory.

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

#209

Earlier quoted context omitted.

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.

When talking C++ move semantics it's easy to talk past each other. So I'm not sure what your claim is. Another commenter said that one can tell if something is moved or not without looking at the body of the callee. Is that what you're saying? Because you can't.

I apologize if you're making a different claim, but I'm not clear on what that is.

Anyway, for my point, here's an example where neither copy nor move happens, which one can only know by looking at the body of the callee: https://godbolt.org/z/d7f6MWcb5

By changing only the callee we can cause a move: https://godbolt.org/z/b8M495Exq

Equally we can remove the use of `std::move` in the callee, and now it's instead a copy. (of course, in this example with `unique_ptr`, it means a build failure as `unique_ptr` is not copyable)

> [assignment or construction of a variable] happens at one place

Not sure what you mean by that. The callee taking an rvalue reference could first copy, then move, if it wants to. Or do neither (per my example above). Unlike in Rust, the copy/move doesn't get decided at the call point.

You can, at one point, statically determine if the (usually const) single ampersand reference function is called, or the rvalue reference function, via standard polymorphism. But that's not the point where the move cons/assign happens, so for that one has to look in the callee.

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

#210

Earlier quoted context omitted.

This is exactly what I meant as irrelevant. If I call `foo(std::move(my_unique_ptr))`, I know for sure, statically, that my_unique_ptr was moved from, as part of the function call process, and I can no longer access it. Whether `foo` chooses to further move from it is irrelevant.

The only thing that is statically known here is that you’re wrong. The function I posted only moves its parameter half the time, at random. You may want to treat it as moved-from either way, but factually that’s just not what is happening.

Yes, looking at some of the code others have shared, I realized where my confusion and mistake was.

I mistakenly treated in my mind `foo(obj&& x)` as `foo(obj x)`, which would indeed move or copy statically. But with functions that take an r-value reference, you're of course absolutely right, it becomes impossible to determine statically if they will move or not.

Post reply on HN