Move, simply
herbsutter.com
Move, simply
1–10 of 96 posts
Re: Move, simply
#2- C++ moves are not destructive (as compared to move semantics in Rust). This means that types must arrange for a valid "moved-from" state. This isn't a huge issue if the type has a natural sentinel value, but not all types have one and I often end up wrapping class members in std::optional to arrange for one. Frankly, this is annoying, because I now have to pay the overhead of std::optional for no good reason (and also use a C++17 compiler, which is not always available).
- the syntax for rvalue references and perfect forwarding references is the same, which is the cause for much confusion, especially among beginners. I happily used move semantics for a year before I realized that rvalue references and perfect forwarding references are distinct concepts.
Re: Move, simply
#3Move semantics are a great addition to C++, but off the top of my head, there are two warts: - C++ moves are not destructive (as compared to move semantics in Rust). This means that types must arrange for a valid "moved-from" state. This isn't a huge issue if the type has a natural sentinel value, but not all types have one and I often end up wrapping class members in std::optional to arrange for one. Frankly, this i…
Using an object that has been moved from is only required not to lead to a crash - the object needs to be in a "valid but unspecified state". If there's no sentinel value, you can leave the object in pretty much any state at all, and it's on the caller to not do anything silly before resetting it. There's also https://clang.llvm.org/extra/clang-tidy/checks/bugprone-use-..., which will detect any such silliness.
Re: Move, simply
#4Move semantics are a great addition to C++, but off the top of my head, there are two warts: - C++ moves are not destructive (as compared to move semantics in Rust). This means that types must arrange for a valid "moved-from" state. This isn't a huge issue if the type has a natural sentinel value, but not all types have one and I often end up wrapping class members in std::optional to arrange for one. Frankly, this i…
Re: Move, simply
#5Move semantics are a great addition to C++, but off the top of my head, there are two warts: - C++ moves are not destructive (as compared to move semantics in Rust). This means that types must arrange for a valid "moved-from" state. This isn't a huge issue if the type has a natural sentinel value, but not all types have one and I often end up wrapping class members in std::optional to arrange for one. Frankly, this i…
Re: Move, simply
#6Is it not the case that one of the main reasons for the language giving us the ability to write an explicit no-argument constuctor is to address this sort of problem on construction: it allows us to put the object in an application- or library-defined state, thus establishing a convention that helps with correct use? And is it not a common idiom, when writing a move constructor for a class that has an explicit no-argument constructor, to leave the moved-from object in the same state as if it were newly constructed without arguments? (e.g. std::mutex, where that state is unlocked.) (Update: another common idiom is to swap source and destination.) These are the some of the conventions that make an explicit move robust.
Re: Move, simply
#7Move semantics are a great addition to C++, but off the top of my head, there are two warts: - C++ moves are not destructive (as compared to move semantics in Rust). This means that types must arrange for a valid "moved-from" state. This isn't a huge issue if the type has a natural sentinel value, but not all types have one and I often end up wrapping class members in std::optional to arrange for one. Frankly, this i…
>C++ moves are not destructive, as they are in Rust. This means that types must arrange for a "moved-from" state Using an object that has been moved from is only required not to lead to a crash - the object needs to be in a "valid but unspecified state". If there's no sentinel value, you can leave the object in pretty much any state at all, and it's on the caller to not do anything silly before resetting it. There's…
So the fact that moves are not destructive is indeed very limiting.
Re: Move, simply
#8Move semantics are a great addition to C++, but off the top of my head, there are two warts: - C++ moves are not destructive (as compared to move semantics in Rust). This means that types must arrange for a valid "moved-from" state. This isn't a huge issue if the type has a natural sentinel value, but not all types have one and I often end up wrapping class members in std::optional to arrange for one. Frankly, this i…
That's not going to help you unless you explicitly unset the optional in your move constructor. std::optional's move constructor doesn't unset the argument (see #3 in https://en.cppreference.com/w/cpp/utility/optional/optional).
But if you have to implement your own constructors to emplace/unset the optionals, you can just have a sentinel state for your whole object.
Re: Move, simply
#9I'm a huge proponent that classes should be fully configured on construction and should stay that way until the end of their life-cycle. I really dislike classes that are only partially configured on construction, and then you have to call other methods to complete their initialization. Then every method in the class has to have guard code to check whether the object has been fully initialized. And if you really want to code defensively users of the class also have to handle the case where an instance of that class hasn't been fully initialized, either by checking before calling a method, or by catching exceptions . It complicates things for both the user and the implementator of the class, not to mention adding computational overhead on both sides of the API.
If you require objects to be usable after they are moved from then, every object with a move constructor will either need to do extra work during the move to keep itself in a valid (but different) state, or it will need to support being in a partially configured state. In some cases it will be inexpensive to create a valid state (say change an object to point to a preallocated singleton), but in others it would completely defeat the benefit of doing a move to begin with, so you are back to supporting partially configured objects.
In the end this seems like a lot of work to keep moved objects valid, when in practice the vast majority of moved objects are implicitly moved temporaries, that are impossible to used after they are moved. And for the cases where an object was explicitly moved, the misconception that you shouldn't use an object after moving it is already well ingrained enough that it rarely happens by accident.
I can see putting in sentinels and asserts to sanity check that objects aren't being used after move (and typically do), but I'd still prefer to treat use-after-move as the bug, than complicate the normal uses of the object to support an unnecessary corner-case.
Edit: Reworded a few sentences for clarity.
Re: Move, simply
#10I get the impression that the question-and-answer section is circling around the issue of the semantic status of a moved-from variable without quite dispatching it. Is a variable, when it is moved from and thus (if implemented properly) in a valid though unspecified state, not semantically in the same sort of state as a constructed but uninitialized integer, where any bit pattern is valid, but if it happens to be zer…