<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p>The move operation must make sure that the object remains valid
      for reassignment and destruction, as it is for uninitialized
      variables. But that should be the only requirements.</p>
    <p>For example std::vector's move operations must set the pointer to
      the data to nullptr, so the destructor doesn't free the memory
      that is in use by the other object that was moved into.</p>
    <p>Currently the move operations also is required by the standard to
      restore all invariants, to make the object have an "unspecified
      but valid state", such as setting the size to 0 in vector, or
      re-allocating memory for sentinel nodes in std::list. This is
      needless work done on an object that you said explicitly in your
      program that you are going to discard anyway. Therefore not
      discarding it and keep using it is logically meaningless.<br>
    </p>
    <p>My proposal is to remove this additional requirement to restore
      the invariants only, not the requirement that the object need to
      be destructible (or reassignable). This is to return to
      performance oriented c++.</p>
    <p>A safe abstraction can always be put around a fast one, but you
      can't make something that is needlessly safe and slower faster.
      That is the domain of other languages to provide a safe and
      potentially slow default.<br>
    </p>
    <div class="moz-cite-prefix">On 16.12.24 19:24, Andre Kostur via
      Std-Proposals wrote:<br>
    </div>
    <blockquote type="cite"
cite="mid:CAPgQXx20AFLYojH4KFR1CfKucfVOwiMQfJQseSuH3knbp4cJxw@mail.gmail.com">
      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      <div dir="auto">If the object is in an invalid state, how do you
        know that it is even destructible?</div>
      <div><br>
        <div class="gmail_quote gmail_quote_container">
          <div dir="ltr" class="gmail_attr">On Mon, Dec 16, 2024 at
            10:15 AM Paul Meckel via Std-Proposals &lt;<a
              href="mailto:std-proposals@lists.isocpp.org"
              moz-do-not-send="true" class="moz-txt-link-freetext">std-proposals@lists.isocpp.org</a>&gt;
            wrote:<br>
          </div>
          <blockquote class="gmail_quote"
style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Hi.<br>
            <br>
            I propose that the requirements of "moved-from" objects
            belonging to the <br>
            standard library are relaxed from "unspecified but valid" to
            <br>
            "unspecified, and potentially invalid" with "the use of
            their state <br>
            results in undefined behavior".<br>
            <br>
            My reasoning for this proposal is this:<br>
            <br>
            Moving from an object occurs in two scenarios only:<br>
            <br>
            1. Moving from a temporary, in which case this doesn't apply<br>
            2. Moving explicitly, for example with std::move<br>
            <br>
            In both cases, the programmer indicates that the moved-from
            object is no <br>
            longer important to them, and that it's resources may be
            used. As such <br>
            the only two valid operations on these objects should be the
            same two <br>
            valid operations as on uninitialized variables, as they are
            conceptually <br>
            the same: assignment and destruction. Anything else makes no
            sense, and <br>
            there is no use case.<br>
            <br>
            The problem is that the c++ standard places requirements on
            these moved <br>
            from objects if they are types of the standard library. For
            example <br>
            std::vector must be in an "unspecified but valid state",
            where valid <br>
            means that invariants must hold for the moved from object.
            This in turn <br>
            means that all operations on that object have to be safe.
            .size() or <br>
            .push_back() need to perform as expected, even though using
            these <br>
            operations on a moved from object is nonsense.<br>
            <br>
            This places a burden on the library implementers to ensure
            invariants <br>
            are not violated, and more importantly it violates the core
            principle of <br>
            c++ as a language: uncompromising performance, even at the
            cost of <br>
            safety. This is why uninitialized primitive type variables
            exist, even <br>
            though initializing them to a default value would prevent a
            lot of bugs. <br>
            c++ is about performance first, and any safety on top of
            that is <br>
            optional and at no cost if not used. This is why we have
            both operator[] <br>
            and member function .at() for std::array.<br>
            <br>
            Inherently, querying the state of, or using the value of a
            moved from <br>
            object is semantically meaningless, as you just declared
            that you don't <br>
            care about it anymore. A case could be made to allow some
            member <br>
            functions, such as .clear() for vector, to remain valid even
            for moved <br>
            from objects as it effectively resets the state; but a more
            semantically <br>
            correct way would be reassignment of a new vector entirely.<br>
            <br>
            Therefore moving from an object and leaving it in an invalid
            state, if <br>
            that saves performance, seems like a reasonable decision.
            Assigning 0 to <br>
            the size member of a vector adds only one extra instruction
            per move <br>
            (but so does default initializing primitive types, and that
            was deemed <br>
            too much also), but for std::list, this means a dynamic
            memory <br>
            allocation for a new sentinel node, which in turn may throw,
            which is <br>
            why the move operations for std::list are not noexcept, even
            though move <br>
            operations should be noexcept. So you did all that work,
            even <br>
            reallocating memory, on an object that you know that the
            programmer <br>
            intends to discard or reassign over. It makes no sense to
            perform that work.<br>
            <br>
            Impact on existing code:<br>
            <br>
            Code using moved-from object's state, such as relying on a
            vector being <br>
            empty after a move is already a logic error, and a bug, as
            the state of <br>
            the vector may not be empty. It is unspecified after all,
            even if in <br>
            practice it very often is empty.<br>
            <br>
            Standard libraries do not need to implement these changes
            immediately, <br>
            as the current safe behavior is also valid for undefined
            behavior.<br>
            <br>
            Correctly written code without the implicit logic error that
            comes with <br>
            using a moved-from object's state remain valid. Exceptions
            are those <br>
            which use a reset like function, such as .clear() for
            container types, <br>
            instead of a reassignment.<br>
            <br>
            <br>
            What do you think?<br>
            <br>
            Best Regards<br>
            <br>
            Paul<br>
            <br>
            -- <br>
            Std-Proposals mailing list<br>
            <a href="mailto:Std-Proposals@lists.isocpp.org"
              target="_blank" moz-do-not-send="true"
              class="moz-txt-link-freetext">Std-Proposals@lists.isocpp.org</a><br>
            <a
href="https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals"
              rel="noreferrer" target="_blank" moz-do-not-send="true"
              class="moz-txt-link-freetext">https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals</a><br>
          </blockquote>
        </div>
      </div>
      <br>
      <fieldset class="moz-mime-attachment-header"></fieldset>
    </blockquote>
  </body>
</html>

