| ▲ | nixpulvis 9 days ago |
| This was one of the most unsatisfying things about learning C++ move semantics. They only kinda move the thing, leaving this shell behind is a nightmare. |
|
| ▲ | flohofwoe 5 days ago | parent | next [-] |
| C++ doesn't have ownership baked into the language like Rust does, and "move semantics" is all about ownership (under the hood it's just a plain old shallow copy both in C++ and Rust). Making the moved from object inaccessible like in Rust would have required static ownership tracking which I guess the C++ committee was afraid to commit to (and once you have that, you're basically halfway to Rust, including the downside of a more restrictive programming model). |
| |
| ▲ | motorest 4 days ago | parent | next [-] | | > Making the moved from object inaccessible like in Rust would have required static ownership tracking which I guess the C++ committee was afraid to commit to (...) I'm not sure the "afraid to commit to" is a valid interpretation. The requirements that the C++ standard specifies for moved-from objects turns that hypothetical issue into a non-problem. In C++, if you move an object then after the move the object must be left in a valid state. That's it. This means the object can be safely destroyed. You are also free to implement whatever semantics your moved-from object has. If you want your moved-from object to throw an exception, you are free to implement that. If instead you want to ensure your moved-from can be reused you are also free to do so. If you want to support zombie objects then nothing prevents you from going that path. It's up to you. The only thing the standard specifies is that once the lifetime of that object ends, it can be safely destroyed. That sounds both obvious and elegant, don't you agree? | |
| ▲ | Dylan16807 5 days ago | parent | prev [-] | | You'd have to mark some functions as deleting their arguments. But I wouldn't really call that ownership. And it shouldn't restrict the language: If the compiler can't solve it statically then it can set a flag or null and check it before calling the destructor. Instead of a guard being built into every destructor use. |
|
|
| ▲ | motorest 4 days ago | parent | prev | next [-] |
| > This was one of the most unsatisfying things about learning C++ move semantics. They only kinda move the thing, leaving this shell behind is a nightmare. I don't know what nightmares you have. The only requirement that C++ specifies for moved-from objects is that they remain valid. Meaning, they can be safely destroyed. You can go way out of your way and reuse an object that was just moved, but that's a decision you somehow made, and you have the responsibility of adding your reinitialization or even move logic to get that object back in shape. That is hardly something that sneaks up on you. |
|
| ▲ | DLoupe 5 days ago | parent | prev | next [-] |
| Since I use move semantics all the time, this is for me the most frustrating thing about C++ full stop. I really wish they'd fix this instead of adding all those compile-time features. |
| |
| ▲ | motorest 4 days ago | parent | next [-] | | > Since I use move semantics all the time (...) Everyone who ever uses C++ uses move semantics all the time,including move elision. It's not an obscure feature. > (...) this is for me the most frustrating thing about C++ full stop. I've been using C++ for years and I have no idea what you could be possibly referring to. The hardest aspect of move semantics is basically the rule of 5. From that point, when you write a class you have the responsibility to specify how you want your class to be moved and how you want your moved-from class to look like, provided that you ensure you leave it in a valid state. That's it. What exactly do you believe needs fixing? | |
| ▲ | catlifeonmars 5 days ago | parent | prev [-] | | How would you fix this in C++? | | |
| ▲ | DLoupe 4 days ago | parent [-] | | By adding syntax and semantics for destructible moves, meaning the moved object is removed from its scope (without calling its destructor.) | | |
| ▲ | motorest 4 days ago | parent [-] | | I've worked with C++ for a number of years, with a few codebases that were >1M LoC. Never did I stumbled upon a situation where an object was moved and an existing symbol became a problem. I wonder what you are doing to get yourself in that situation. | | |
| ▲ | DLoupe 4 days ago | parent [-] | | > I wonder what you are doing to get yourself in that situation. The problem with the current move semantics is that, compared to e.g. Rust: 1) the compiler generates unnecessary code and 2) instead of just implementing class T you must implement a kind of optional<T>. Which means, that after all those years of using smart pointers I find myself ditching them in favor of plain pointers like we did in the 90's. | | |
| ▲ | catlifeonmars 4 days ago | parent | next [-] | | When you say you must, do you mean that it’s best practice, that or that this is UB or similar? | | |
| ▲ | motorest 4 days ago | parent [-] | | > When you say you must, do you mean that it’s best practice, that or that this is UB or similar? I'm not OP, but the only requirements that C++ imposed on moved-from objects is that they remain valid objects. Meaning, they can be safely destroyed or reused by reassigning or even moving other objects into them. I have no idea what OP could be possibly referring to. |
| |
| ▲ | motorest 4 days ago | parent | prev [-] | | > The problem with the current move semantics is that, compared to e.g. Rust: 1) the compiler generates unnecessary code and 2) instead of just implementing class T you must implement a kind of optional<T>. I don't know what you mean by "compiler generates unnecessary code" or why you see that as a problem. I also have no idea what you mean by "a kind of optional". The only requirement on moved-from objects is that they must be left in a valid state. Why do you see that as a problem? | | |
| ▲ | DLoupe 3 days ago | parent [-] | | The compiler generates code for calling the destructor after the object was moved. This was problem #1. Regarding #2, take Resource Acquisition Is Initialization (RAII) as an example - in RAII, the existence of an object implies the existence of a resource. Now, if you want to be able to move, the object becomes "either the resource exists or it was moved out". As someone else noted in the comments, this affects not only the destructor. Methods cannot assume the existence of the resource, they have to check it first. Kind of like optional<MyResource>. |
|
|
|
|
|
|
|
| ▲ | tialaramex 5 days ago | parent | prev | next [-] |
| When I looked into the history of the C++ move (which after all didn't even exist in C++ 98 when the language was first standardized) I discovered that in fact they knew nobody wants this semantic. The proposal paper doesn't even try to hide that what programmers want is the destructive move (the thing Rust has) but it argues that was too hard to do with the existing C++ design so... The more unfortunate, perhaps disingenuous part is that the proposal paper tries to pretend you can make the destructive move later if you need it once you've got their C++ move. But actually what they're proposing is that "move + create" + "destroy" = "move". So, that's extra work it's not the same thing at all and sure enough in the real world this means extra work, from compilers, from programmers and sometimes (if it isn't removed by the optimiser) from the runtime program. |
| |
| ▲ | reactordev 5 days ago | parent | next [-] | | C++ is riddled with “good enough” without completeness. Resulting in more bandaids to the language to fix stuff they half implemented in the first place. | |
| ▲ | 4 days ago | parent | prev | next [-] | | [deleted] | |
| ▲ | aw1621107 5 days ago | parent | prev [-] | | > When I looked into the history of the C++ move (which after all didn't even exist in C++ 98 when the language was first standardized) I discovered that in fact they knew nobody wants this semantic. The proposal paper doesn't even try to hide that what programmers want is the destructive move (the thing Rust has) but it argues that was too hard to do with the existing C++ design so... > The more unfortunate, perhaps disingenuous part is that the proposal paper tries to pretend you can make the destructive move later if you need it once you've got their C++ move. For reference, I think N1377 is the original move proposal [0]. Quoting from that: > Alternative move designs > Destructive move semantics > There is significant desire among C++ programmers for what we call destructive move semantics. This is similar to that outlined above, but the source object is left destructed instead of in a valid constructed state. The biggest advantage of a destructive move constructor is that one can program such an operation for a class that does not have a valid resourceless state. For example, the simple string class that always holds at least a one character buffer could have a destructive move constructor. One simply transfers the pointer to the data buffer to the new object and declares the source destructed. This has an initial appeal both in simplicity and efficiency. The simplicity appeal is short lived however. > 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. <snip> > In the end, we simply gave up on this as too much pain for not enough gain. However the current proposal does not prohibit destructive move semantics in the future. It could be done in addition to the non-destructive move semantics outlined in this proposal should someone wish to carry that torch. [0]: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n13... | | |
| ▲ | binary132 5 days ago | parent [-] | | Now that would be a cool first proposal and implementation. I wonder if there’s any prior art in C++ yet. | | |
| ▲ | aw1621107 5 days ago | parent [-] | | If there is any prior art I'm not aware of it. The problems described in the part I snipped out around how destructive moves would work with class hierarchies sound thorny, for what it's worth. |
|
|
|
|
| ▲ | triknomeister 5 days ago | parent | prev [-] |
| Destructive vs non-destructive move. |