Why is a std::move of a value from a const std::optional possible? [duplicate]
Asked Answered
J

1

13

Why is the following snippet legal C++ code? This is a purely theoretical question - there is no usecase that I have in mind:

#include <optional>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> v{1, 2, 3};
    const std::optional<std::vector<int>> ov = v;
    const auto nv = std::move(ov.value());

    for (const auto& x : *ov) { std::cout << x; }
    for (const auto& x : nv) { std::cout << x; }
}

This yields 123123, but I do not understand the reason.

  1. Why is a std::move applied to a value of a const optional legal?
  2. Why does the optional ov still hold the vector?

Does ov.value() internally create a copy to a temporary which is then moved from?

Jarret answered 10/5, 2023 at 10:22 Comment(8)
Think of std::move() as a cast that does nothing for const objects. Nothing is moved here.Alf
Note that since the move constructor of vector takes an non-const rvalue ref, the construction of nv is done using the copy constructor.Metalliferous
When using std::move, I like to follow a policy that the object moved from is in a valid-but-unspecified state (suitable for re-assignment or destruction). In general. In the OP code, since ov is const, the std::move will not result in the object being gutted and left behind with an empty husk. Regardless, I find following the idiom that the (potentially) moved-from object is as-if it were an ex-parrot works well. In general. (There are exceptions, such as moving from a std::unique_ptr does leave it in a well specified state.)Mouthful
std::vector<T> is also an exception - a moved-from vector is guaranteed to be empty.Wordbook
@Wordbook That's not an exception. empty is a fine example of a valid-but-unspecified state.Dahomey
@MooingDuck This makes no sense. Empty is a perfectly specified state. Otherwise, after doing std::vector::clear() the resulting vector would have been useless.Wordbook
@Evg: Moving in general leaves objects in a valid but unspecified state, because moving objects in general can't make specific claims about every possible class you might write. However, almost every class in the C++ standard library does specify a state for that specific class, which is usually but not always the "empty" state.Dahomey
For std::vector<T> it is always an empty state.Wordbook
A
18

Why is std::move applied to a value of a const optional legal?

Sure, why not? All that std::move is, is a cast to an rvalue reference. You'll just end up with an rvalue reference to a const object. That's nothing unusual.

Why does the optional ov still holds the vector?

Because it is const, and cannot be changed.

Several conditions must happen in order for an object to be moved. std::move takes care of the most important one, but all others must be satisfied as well.

It is a common misconception that spraying std::move everywhere guarantees a super-duper-efficient move of an object from one place to another, by magic. That's not true, that's not what std::move is, it's just a cast that's sometimes needed in order to move something. But that's not everything that's needed, just the most important part.

Anemone answered 10/5, 2023 at 10:48 Comment(3)
This answer is correct, but seems to try hard to avoid saying the obvious thing: move is a misnomer, so its users should ignore the name when trying to remember what this function actually does.Bio
I would argue that a "rvalue reference to a const object" is "something unusual", a rvalue reference generally represents an object that is ok to plunder, but a const object cannot be plundered.Tetherball
You emphasise (by repeating twice) that std::move is not everything that's needed for a move, but can you name the other things that are needed? A link to a reference would be sufficient.Monospermous

© 2022 - 2024 — McMap. All rights reserved.