Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 859
Holt Avatar asked Sep 01 '17 13:09

Holt


2 Answers

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.

like image 180
sp2danny Avatar answered Oct 06 '22 00:10

sp2danny


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.

like image 45
T.C. Avatar answered Oct 05 '22 23:10

T.C.