4

The standard mandates that the move assignment operator of optional ...

constexpr optional& operator=( optional&& other )

[...] shall not participate in overload resolution unless is_move_constructible_v<T> is true and is_move_assignable_v<T> is true.

The assignment of an optional value lhs = rhs; does either

  • destroy lhs (if bool(lhs) && !bool(rhs))
  • construction lhs from rhs (if !bool(lhs) && bool(rhs)) or
  • assign rhs to lhs (if bool(lhs) && bool(rhs)).

Thus, it would have been an option to have two sets of preconditions for move assignment of optional:

  1. is_move_constructible_v<T> && is_move_assignable_v<T>
  2. is_move_constructible_v<T> && is_copy_assignable_v<T>

Where the second form could use copy assignment if bool(lhs) && bool(rhs) but move construction if !bool(lhs) && bool(rhs).

I see an admittedly rather artificial issue with the current set of preconditions with respect to the following two classes of types:

  1. A type which is not move assignable but copy assignable, move constructible and copy constructible can not benefit from move construction on assignment, eventhough construction is part of the assignment operation. The optional copy assignment operator will be selected and copy construct or copy assign the value.

  2. A type which is neither copy constructible nor move assignable but move constructible and copy assignable cannot be assigned at all.

Is this something that has been considered during the standardization process for optional or is there any rationale why it has not been considered or has been waived?

(Disclaimer: I know that is_move_assignable is usually true if is_copy_assignable is true unless the move assignment operator is explicitly deleted.)

Pixelchemist
  • 24,090
  • 7
  • 47
  • 71
  • 3
    `is_move_constructible_v && is_move_assignable_v == is_move_constructible_v && is_copy_assignable_v` so it's not needed. As show [here](https://en.cppreference.com/w/cpp/types/is_move_assignable) an implicitly deleted move assignment operator is still move assignable as long as the copy assignment operator is not deleted. – NathanOliver Mar 18 '19 at 21:00
  • @NathanOliver: That's what my disclaimer says. Explicitly deleting the move assignment operator makes `is_move_assignable` false, regardless of the presence of copy assignment. There is a reason I wrote that the problem is "rather artificial". – Pixelchemist Mar 18 '19 at 21:05
  • 1
    Why would you explicitly delete the move operations but allow the copy? – NathanOliver Mar 18 '19 at 21:06
  • 4
    If the problem is really so artificial, as you admit, then it doesn't make sense for the standard to increase the effort required of library implementers by forcing them to sometimes use the copy assignment operator instead of the move assignment operator. Or, for that matter, to complicate the standard itself by codifying this requirement. – Brian Bi Mar 18 '19 at 21:08
  • 5
    If you have a type that is copy-{construct,assign}able but not move-{construct,assign}able, you're doing something very strange, and I'm not sure that it's worth complicating the library to support such a thing. – Barry Mar 18 '19 at 21:17
  • Also for the library concepts, `CopyConstructible` [subsumes](http://eel.is/c++draft/concept.copyconstructible) `MoveConstructible`. – Barry Mar 18 '19 at 21:30
  • 1
    As a general rule, the library could not care less about copyable-but-not-movable abominations. – T.C. Mar 19 '19 at 00:03
  • @T.C.: Technically, the suggested issue is worse than that; it's move-constructible, but not move-assignable. – Nicol Bolas Mar 19 '19 at 00:46
  • As a general rule, at least one library implementer thinks such types should be burnt and then thrown in the sea. And he's absolutely certain it's not worth complicating the library to support such things. Just throw them in the sea and forget about them. – Jonathan Wakely Jul 14 '19 at 22:49
  • @JonathanWakely : Let me agree but also add that I think such types might actually exist (either by mistake or ignorance). – Pixelchemist Jul 14 '19 at 23:00

1 Answers1

5

You should consider using std::optional::emplace for move-constructible types (second case)

cos
  • 940
  • 1
  • 10
  • 25