为什么当 T 不可移动构造时 std::optional 的移动构造函数不被删除?

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

根据标准,std::optional<T>的拷贝构造函数:

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

但是std::optional<T>的移动构造函数:

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

正如我 understand deleted constructors,不删除 std::optional<T> 的移动构造函数的目的是允许这样的代码:

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

...依靠某些转换序列工作 - o2 将由类型 A 的对象构造,该对象已使用 std::optional<X>&& 构造(如果我我错了)。

但是关于 std::optional 的可能构造函数,我很难找到一个可以匹配这个用例的构造函数...

如果 T 不可移动构造,为什么 std::optional<T> 的移动构造函数根本没有 删除

明确删除意味着它将是最匹配的 x 值,从而导致编译时错误,而不是 复制构造函数处理这些情况。

例如:

#include <utility>

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

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

这将导致如下结果:

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

显式删除的函数仍然参与重载 分辨率,并且可以是最佳匹配。这可能有用, 例如禁用某些转换。

委员会真的不关心可复制但不可移动的可憎行为。参见,例如,LWG issue 2768 的讨论,其中描述了 "pathological" 等类型的特征,以及早期尝试支持它的 "madness".

一般情况下,此类内容的默认措辞是 "shall not participate in overload resolution",除非有某些特殊原因来捕获调用(有时是合适的 - 例如,LWG issue 2766 - but can causes undesirable side effects such as LWG issue 2993)。对于copy special members,在concepts之前根本做不到,所以必须使用"defined as deleted"。对于 move 特殊成员,OTOH,"defined as deleted" 不够精确,因为 "explicitly deleted move" 和 "defaulted move that is implicitly defined as deleted" 之间存在巨大差异:后者不参与重载决议。

另见 LWG issue 2958 的讨论。