为什么当 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 的讨论。
根据标准,std::optional<T>
的拷贝构造函数:
...shall be defined as deleted unless
is_copy_constructible_v<T>
istrue
.
但是std::optional<T>
的移动构造函数:
...shall not participate in overload resolution unless
is_move_constructible_v<T>
istrue
.
正如我 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 的讨论。