If we have a type T
that is not copy-constructible but copy-assignable:
struct T {
T() = default;
~T() = default;
T(const T&) = delete;
T(T&&) = default;
T& operator=(const T&) = default;
T& operator=(T&&) = default;
};
then std::is_copy_assignable_v<T>
is obviously true
, but std::is_copy_assignable_v<std::expected<T, int>>
is false
.
This behavior is described on cppreference: std::expected<T,E>::operator=
What is the rationale behind this? Why couldn't we allow std::expected<T, E>
to be copy-assignable if T
is copy-assignable, even if it is not copy-constructible? The same question also applies to move assignability.
The result of the assignment depends on whether
this->has_value()
is true. We can assign an expected holding aT
to an expected holding anE
. In the case of holding an error, there is no "target T" to assign to, and we must copy construct to obtain a value.Simple illustration:
Since this is all determined at runtime, both conditions must hold for the member to be well-defined on all code paths.