Live data from Hacker News

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

0xghost.dev

61–70 of 220 posts

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

#61
post #53
post #49

Earlier quoted context omitted.

Of course it is not reserved for library writers - nothing is. But it is not a feature that application writers should worry about overmuch.

std::move is definitely for there for optimizing application code and is often used there. another silly thing you often see is people allocating something with a big sizeof on the stack and then std::moving it to the heap, as if it saves the copying

> another silly thing you often see is people allocating something with a big sizeof on the stack and then std::moving it to the heap, as if it saves the copying

never seen this - an example?

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

#62

Earlier quoted context omitted.

The reason performance-noexcept-move-constructor is not enabled by default is likely because blindly applying noexcept is dangerous if the underlying logic isn't actually exception-free. If you let clang-tidy slap noexcept on a move constructor that does end up throwing (perhaps because it calls into a legacy member or allocates memory internally), the runtime behavior changes from caught exception to std::terminate(…

Exceptions should never be enabled by default. We live in a 64bit world so allocations failing indicates some other problem.

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

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

#63

> This code works. It compiles. It runs. But depending on how you’ve implemented your types, it might be performing thousands of expensive copy operations instead of cheap moves without you realizing it. I've spent the last two decades in the .net platform. But for a decade or so before that I was a C++/Unix dev. I remember old style "C with classes" C++ as being fairly small and elegant, and approximately as easy to…

> So, what are the forces that have determined the current state of C++?

I'm very confident that the main driving factors are:

1. "performance" (not wanting to do more allocations than necessary)

2. abi compatibility

3. adding features without caring how well they integrate

Example for 1:

"emplace", you normally have "append" but emplace directly constructs the object in the container instead of having to be constructed first and then moved into the container. This nice and all but breaks when using pairs (for reasons you can google but I don't wanna explain here). So now you have these obscure classes like https://en.cppreference.com/w/cpp/utility/piecewise_construc... which solve this.

Example for 2:

Basically they never break the ABI and this leads to tons of old stuff hanging around and never being changed and just more stuff being added on top. std::iostream is famously slow and a big reason is because you can't fix it without breaking the abi which they don't wanna do.

Example for 3:

The whole template thing adds so much complexity it's bonkers, I think c++ without templates would be pretty manageable comparatively. For example because C++ has constructors and they don't quite mix well with templates you suddenly end up in the situation that you have 2 concepts: "normal" template argument deduction and constructor template argument deduction (CTAD). Because of this asymmetry you need a custom language feature called "deduction guides" to maneuver yourself out of the problems that come from this.

Or another short one: std::expected without something like the "!" that rust has. You end up with endless "if(result.has_value()) { return result; }" cascades and it's horribly unergonomic. So now we have a Result class but it's practically unusable that it will only fragment the ecosystem even more.

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

#64
post #60

> [std::move silently copies const values, because] If something is const, you can’t move from it by definition. Whoever wrote that definition should have a thing or two to learn from Rust. Different language I know, but it proves that it wasn't needed to cause so much confussion and collectively so much time and performance lost. Also, who writes rules like that and ends the day satisfied with the result? It seems u…

There was no Rust in 2011.

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

#65
post #30
post #26

Earlier quoted context omitted.

After moving a value, it needs to remain in a "valid but unspecified state". How do you mean accessing a valid object is UB?

>After moving a value, it needs to remain in a "valid but unspecified state". No, it doesn't. The standard library requires that for its classes, but not the language. "Unless otherwise specified, such moved-from objects shall be placed in a valid but unspecified state."[0] [0] https://timsong-cpp.github.io/cppwp/n4950/lib.types.movedfro...

Ok, fair enough.

So you're saying if you use the language to write UB, then you get UB?

Seems kinda circular. Ok, you're not the same user who said it can be UB. But what does it then mean to same "sometimes it's UB" if the code is all on the user side?

"Sometimes code is UB" goes for all user written code.

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

#66
post #60

> [std::move silently copies const values, because] If something is const, you can’t move from it by definition. Whoever wrote that definition should have a thing or two to learn from Rust. Different language I know, but it proves that it wasn't needed to cause so much confussion and collectively so much time and performance lost. Also, who writes rules like that and ends the day satisfied with the result? It seems u…

What’s the problem? It makes perfect sense to me that a const object cannot be moved from, since it violates the constness. Since constness goes hand in hand with thread safety you really don’t want that violation.

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

#67
post #14

Earlier quoted context omitted.

My function can choose to move or not to move from an object based on io input.

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 expectation is that `some_other_function` is always called with `{ 2 }`, but this will only happen if `some_function` actually moves `a`.

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

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

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 rules like C++ move constructor hell is an inevitable outcome of irreducible complexity in systems programming. Put another way: C is also a systems programming language that works for many people, and it doesn’t have any of these Byzantine rules (unless you build them yourself). That’s not to say C is better/preferable, but it swims in the same “official Big Gun systems language” pond as C++, which seems to indicate that revalue semantics as complex as C++’s are a choice, not an inevitability.

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

#69

> This code works. It compiles. It runs. But depending on how you’ve implemented your types, it might be performing thousands of expensive copy operations instead of cheap moves without you realizing it. I've spent the last two decades in the .net platform. But for a decade or so before that I was a C++/Unix dev. I remember old style "C with classes" C++ as being fairly small and elegant, and approximately as easy to…

Note that C# 14 versus C# 1.0 isn't suffering from feature creap as well.

What has guided C++ are the 300+ volunteers that get to submit papers, travel around the world attending the meetings, and win the election rounds of what gets into the standard.

Unfortunately design by committee doesn't lead to a clear product roadmap.

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

#70

Earlier quoted context omitted.

> Can you show an example of what you mean? void foo(std::unique_ptr && p) { std::random_device rdev {}; auto dist = std::uniform_int_distribution (0, 1); if (dist(rdev)) { auto pp = std::move(p); } }

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.

No: https://godbolt.org/z/d7f6MWcb5

Look, the act of calling std::move and and calling a function taking an rvalue reference in no way invokes a move constructor or move assignment. It does not "move".

It's still just a reference, albeit an rvalue reference. std::move and the function shape is about the type system, not moving.

(Edit: amusingly, inside the callee it's an lvalue reference, even though the function signature is that it can only take rvalue references. Which is why you need std::move again to turn the lvalue into rvalue if you want to give it to another function taking rvalue reference)

I didn't reply to this thread until now because I thought you may simply be disagreeing about what "move" means (I would say move constructor or move assignment called), but the comment I replied to makes a more straightforward factually incorrect claim, that can easily be shown in godbolt.

If you mean something else, please sketch something up in godbolt to illustrate your point. But it does sound like you're confusing "moving" with rvalue references.

Edit: for the move to happen, you have to actually move. E.g. https://godbolt.org/z/b8M495Exq

Post reply on HN