C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Relax requirements on moved from objects in standard library

From: Sebastian Wittmeier <wittmeier_at_[hidden]>
Date: Mon, 16 Dec 2024 19:38:14 +0100
The actual move is not done by std::move, but (only) possibly by assigning it/initializing with it or calling a function with it as parameter. std::move creates an rvalue reference.   So std::move just states the readiness to move, not the explicit move itself.   What you are describing is relocation. There are efforts underway to get relocation into the C++ language. Relocation moves from and destroys the original object. It may not be accessed afterwards. That leads to potential performance optimizations.   -----Ursprüngliche Nachricht----- Von:Paul Meckel via Std-Proposals <std-proposals_at_[hidden]> Gesendet:Mo 16.12.2024 19:15 Betreff:[std-proposals] Relax requirements on moved from objects in standard library Anlage:OpenPGP_0x3347986B64D7F77D.asc, OpenPGP_signature.asc An:std-proposals_at_[hidden]; CC:Paul Meckel <paul_meckel_at_[hidden]>; Hi. I propose that the requirements of "moved-from" objects belonging to the standard library are relaxed from "unspecified but valid" to "unspecified, and potentially invalid" with "the use of their state results in undefined behavior". My reasoning for this proposal is this: Moving from an object occurs in two scenarios only: 1. Moving from a temporary, in which case this doesn't apply 2. Moving explicitly, for example with std::move In both cases, the programmer indicates that the moved-from object is no longer important to them, and that it's resources may be used. As such the only two valid operations on these objects should be the same two valid operations as on uninitialized variables, as they are conceptually the same: assignment and destruction. Anything else makes no sense, and there is no use case. The problem is that the c++ standard places requirements on these moved from objects if they are types of the standard library. For example std::vector must be in an "unspecified but valid state", where valid means that invariants must hold for the moved from object. This in turn means that all operations on that object have to be safe. .size() or .push_back() need to perform as expected, even though using these operations on a moved from object is nonsense. This places a burden on the library implementers to ensure invariants are not violated, and more importantly it violates the core principle of c++ as a language: uncompromising performance, even at the cost of safety. This is why uninitialized primitive type variables exist, even though initializing them to a default value would prevent a lot of bugs. c++ is about performance first, and any safety on top of that is optional and at no cost if not used. This is why we have both operator[] and member function .at() for std::array. Inherently, querying the state of, or using the value of a moved from object is semantically meaningless, as you just declared that you don't care about it anymore. A case could be made to allow some member functions, such as .clear() for vector, to remain valid even for moved from objects as it effectively resets the state; but a more semantically correct way would be reassignment of a new vector entirely. Therefore moving from an object and leaving it in an invalid state, if that saves performance, seems like a reasonable decision. Assigning 0 to the size member of a vector adds only one extra instruction per move (but so does default initializing primitive types, and that was deemed too much also), but for std::list, this means a dynamic memory allocation for a new sentinel node, which in turn may throw, which is why the move operations for std::list are not noexcept, even though move operations should be noexcept. So you did all that work, even reallocating memory, on an object that you know that the programmer intends to discard or reassign over. It makes no sense to perform that work. Impact on existing code: Code using moved-from object's state, such as relying on a vector being empty after a move is already a logic error, and a bug, as the state of the vector may not be empty. It is unspecified after all, even if in practice it very often is empty. Standard libraries do not need to implement these changes immediately, as the current safe behavior is also valid for undefined behavior. Correctly written code without the implicit logic error that comes with using a moved-from object's state remain valid. Exceptions are those which use a reset like function, such as .clear() for container types, instead of a reassignment. What do you think? Best Regards Paul -- Std-Proposals mailing list Std-Proposals_at_[hidden] https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2024-12-16 18:40:20