Why is the move-constructor of std::optional not deleted when T is not move-constructible?

2.7k views Asked by At

According to the standard, the copy-constructor of std::optional<T>:

...shall be defined as deleted unless is_copy_constructible_v<T> is true.

But the move-constructor of std::optional<T>:

...shall not participate in overload resolution unless is_move_constructible_v<T> is true.

As I understand deleted constructors, the purpose of not-deleting the move-constructor of std::optional<T> would be to allow code like this:

std::optional<X> o1;
std::optional<X> o2(std::move(o1));

...to work relying on some conversion sequence - o2 would be constructed by an object of type A that has been constructed using a std::optional<X>&& (correct me if I am wrong).

But regarding the possible constructors of std::optional, I have a hard time figuring out one that could match this use case...

Why is the move-constructor of std::optional<T> simply not deleted if T is not move-constructible?

2

There are 2 answers

6
sp2danny On BEST ANSWER

To explicitly delete it means that it will be the best match for x-values, and thus result in a compile-time error, rather than the copy-constructor taking those cases.

Ex:

#include <utility>

struct X
{
    X() = default;
    X(const X&) = default;
    X(X&&) = delete;
};

int main()
{
    X a;
    X b(std::move(a));
}

This will result in something like:

'X::X(X &&)': attempting to reference a deleted function

Explicitly deleted function still participate in overload resolution, and can be the best match. This can be useful, to disable certain conversions for example.

0
T.C. On

The committee really doesn't care about copyable-but-not-movable abominations. See, e.g., the discussion of LWG issue 2768, which characterized such types as "pathological" and an earlier attempt to support it as "madness".

The default wording for this kind of stuff in general is "shall not participate in overload resolution", unless there's some particular reason to trap the call (which is sometimes appropriate - e.g., LWG issue 2766 - but can causes undesirable side effects such as LWG issue 2993). For the copy special members, that simply can't be done before concepts, so "defined as deleted" had to be used. For move special members, OTOH, "defined as deleted" is not sufficiently precise, because there's a huge difference between "explicitly deleted move" and "defaulted move that is implicitly defined as deleted": the latter doesn't participate in overload resolution.

See also the discussion of LWG issue 2958.