12

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?

Holt
  • 36,600
  • 7
  • 92
  • 139

2 Answers2

11

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.

sp2danny
  • 7,488
  • 3
  • 31
  • 53
  • So it's only to allow construction of `optional` from x-values when `T` is copyable but not movable? – Holt Sep 01 '17 at 13:47
  • @Holt: exactly. – sp2danny Sep 01 '17 at 13:54
  • How should the move-constructor be declared to exclude it from overload resolution if `T` is not move-constructible? As `= default`? – Maarten Bamelis Sep 01 '17 at 13:54
  • @MaartenBamelis With some type traits and possibly SFINAE – Passer By Sep 01 '17 at 13:55
  • 1
    @MaartenBamelis It depends on how you implement `optional` - You cannot (always) store an instance of `T` directly into the `optional` object due to some restrictions (in particular if `T` is not default-constructible), so using `= default` within `optional` will not make much sense. You'll have to rely on type traits and SFINAE. – Holt Sep 01 '17 at 13:57
  • 2
    @MaartenBamelis: partial specialization is probably best, see [this question](https://stackoverflow.com/questions/27073082/conditionally-disabling-a-copy-constructor) – sp2danny Sep 01 '17 at 13:59
5

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.

T.C.
  • 133,968
  • 17
  • 288
  • 421