Why does moving std::optional not reset state

I was rather surprised to learn that the move constructor (and assignment for that matter) of std::optional does not reset the optional moved from, as can be seen in [19.6.3.1/7] which states "bool(rhs) is unchanged."

This can also be seen by the following code:

#include <ios>
#include <iostream>
#include <optional>
#include <utility>

int main() {
  std::optional<int> foo{ 0 };
  std::optional<int> bar{ std::move(foo) };

  std::cout << std::boolalpha
            << foo.has_value() << '\n'  // true
            << bar.has_value() << '\n'; // true
}

This seems to contradict other instances of moving in the standard library such as with std::vector where the container moved from is usually reset in some way (in vector's case it is guaranteed to be empty afterwards) to "invalidate" it even if the objects contained within it themselves have been moved from already. Is there any reason for this or potential use case this decision is supposed to support such as perhaps trying to mimic the behavior of a non-optional version of the same type?

Unless otherwise specified, a moved-from object of class type is left in a valid but unspecified state. Not necessarily a "reset state", and definitely not "invalidated".

For primitive types , moving is the same as copying, i.e. the source is unchanged.

The defaulted move-constructor for a class type with primitive members will move each member, i.e. leave the primitive members unchanged; a user-defined move constructor might or might not "reset" them.

A moved-from vector may or may not still have elements in it. We would expect it not to, since that's efficient, but it cannot be relied on.

A moved-from std::string may still have elements in it, because of Small String Optimization.


move on std::optional is actually specified by the standard (C++17 [optional.ctor]/7). It is defined as doing move on the contained type, if present. It does not turn a valued optional into a valueless optional.

So it is actually expected that your code outputs true true, and the actual contained value in foo should stay the same too.


Regarding the question of why std::optional 's move-constructor is defined this way: I can't say for sure; but an optional is not like a vector with max size of 1. It's more like a variable with a validity flag tacked on. Accordingly, it makes sense for moving an optional to be like moving the variable.

If moving an optional left the old one "empty", then a = std::move(b); would invoke the destructor of b's managed object, which would be unexpected (to me, at least).

c++ - Why does moving std::optional not reset state, Unless otherwise specified, a moved-from object of class type is left in a valid but unspecified state. Not necessarily a "reset state", and  Why does moving std::optional not reset state? In a word: Performance. One of the chief motivating reasons for move semantics to exist in the first place is performance. So the special operations move construction and move assignment should be as fast as possible for all types

In a word: Performance.

One of the chief motivating reasons for move semantics to exist in the first place is performance. So the special operations move construction and move assignment should be as fast as possible for all types.

In order to assist this goal, it is standard practice that moved-from objects be left in a valid but unspecified state. So the very minimum that optional move construction/assignment need to do is to move from the source argument. To specify setting the source to not have a value after the move is equivalent to saying:

After you move, do some extra, unnecessary work.

No matter how small that extra work is, it is non-zero. Some (and I dare say many) clients will not need that extra work, and should not have to pay for it. Clients who do need it can easily add x.reset() after the move, putting the moved-from optional into a well-specified state.

Quick Q: Why does moving std::optional not reset state? : Standard , Quick Q: Why does moving std::optional not reset state? One of the chief motivating reasons for move semantics to exist in the first place is  22 Why does moving std::optional not reset state; 9 use goto inside function php; 8 "Normal" Blend Mode with OpenGL Trouble; 8 Is it OK to insert an empty into

What that paragraph says is that if that optional had a value, it still has a value. Since that value has been moved from (to the newly constructed object), it could be a different value than what it had before the move. This allows you to access the moved-from optional object the same way as a moved-from non-optional object, so the behavior of a T vs. optional<T> (when it contains an object) when accessed after the move is the same.

Also, the overall effect of a move from an optional depends on how the contained type T handles a move. Other classes (like vector) do not have this dependency.

std::optional, Swap, forward and move. swap The class template std::optional manages an optional contained value, i.e. a value that may or may not be The object does not contain a value in the following conditions: The member function reset() is called. T, -, the type of the value to manage initialization state for. The object does not contain a value in the following conditions: The object is default-initialized. The object is initialized with/assigned from a value of type std::nullopt_t or an optional object that does not contain a value. The member function reset() is called.

While it might be reasonable to expect that std::optional behaves similarly to std::unique_ptr which resets state of moved-from object, there are reasons not to demand such behavior. I think one of them is that std::optional of a trivial type should be a trivially copyable type. As such, it cannot have a non-defaulted move constructor and cannot reset its has_value flag.

Having std::optional for a non-trivial type behave differently from std::optional for a trivial type is a rather bad idea.

Standard C++ on Twitter: "Quick Q: Why does moving std::optional , Quick Q: Why does moving std::optional not reset state? http://bit.ly/2QHRxBm #​cpp. 11:59 AM - 3 Jun 2019. 2 Retweets; 3 Likes; Alexander Petrov · Quijote, the​  Even though the move is temporary, when working in a state, you should expect to pay taxes there. Make sure you have withholding for that state’s taxes while you’re working there. You will be filing taxes as a non-resident in that state and as a resident in your own state.

Using C++17 std::optional, std::optional is a part of C++ vocabulary types along with std::any , std::variant For example, a resource type has no default constructor, and the As it best suits the cases when the value is empty and it's a normal state of the program. If you assign (or reset) with a nullopt then if the optional contains a  The compressor of a fridge utilizes a gaseous refrigerant that is in a low-pressure state. When the fridge thermostat requests more cold air, the compressor kicks on and the refrigerant becomes high-pressure, moving through the cooling coils while fans push the cool air into the fridge and freezer compartments.

The C++ Standard Library: A Tutorial and Reference, I was rather surprised to learn that the move constructor (and assignment for that matter) of std::optional does not reset the optional moved from, as can be seen  Issue – Apple Mail Moving Messages Stuck “I am facing issues with Apple Mail Moving Messages Stuck regularly. Recently, while working in Apple Mail, the indicator, which is there at the bottom sidebar, says “moving-messages” always. It never goes away, not even when mail re-starts. It seems Apple mail stuck on moving messages.

design decision for std::optional moved from state : cpp, pt.reset() Default constructor; creates a packaged task with no shared state and alloc Move constructor; moves the packaged task rv (task and state) to pt (rv has no the shared state (outcome of the task) Calls the task (with optional arguments) An object of class std::thread, introduced in Section 18.2.1, page 964, is  A couple problems cause this to occur. A carriage jam prevents the carriage from moving across the page, and thus the printer malfunctions. Dirty/faulty encoder strip. If your encoder strip is dirty, dusty, or broken, it will be best to clean it. For additional information, see our Encoder Strip Replacement Repair Guide. Broken printer carriage.

Comments
  • "in vector's case it is guaranteed to be empty afterwards" Um... no it isn't.
  • @NicolBolas Well in most cases, cppreference at least says that the "After the move, other is guaranteed to be empty().", but in the case of using a custom allocator such is not true (but I wasn't talking about using a custom allocator so while technically correct that's not really what I meant).
  • @LemonDrop: Well then cppreference is lying. The standard does not say that, and the standard is what matters.
  • On the state of a moved-from vector: stackoverflow.com/a/17735913/576911
  • @LemonDrop That is not true, however. E.g. a vector<int> could be left with contents {1,2,3,4} with constant time complexity.
  • As an aside, letting a moved-from small string with active SSO retain its contents on being used for move-construction would mean branching. Simply constructing an empty string and swapping sight-unseen is more efficient.
  • Problem is that most(all?) std implementations do the wrong thing with std::string so they give unreasonable expectations to users. :/
  • @NoSenseEtAl: I can only speak for the libc++ std::string. It does indeed leave a moved-from string empty, but not because it is "wrong", but because that is the fastest thing for this design. The libc++ string was actually designed from the move members outward. It copies all of the bits of the string object, and then zeros all of the bits of the source string object. It does not check whether the string is long or short. This copy&zero algorithm is correct whether the string is in the long or short mode. It would be incorrect to avoid the zero when in the long mode.
  • I find it weird that it is faster, since anyway destination string will need to branch on long or short because it needs to know if it is storing pointers or internal buffer, but I trust you. :)
  • Another nice thing about libc++ is that its source is so easily inspectable. ;-) github.com/llvm-mirror/libcxx/blob/master/include/…
  • The "moving from a string" case in libc++ was described (by Howard) here: stackoverflow.com/questions/52696413/… .