为啥 std::optional 不允许“移动构造和仅复制分配”类型的移动分配?

Posted

技术标签:

【中文标题】为啥 std::optional 不允许“移动构造和仅复制分配”类型的移动分配?【英文标题】:Why doesn't std::optional allow move assignment for "move construct and copy assign only" types?为什么 std::optional 不允许“移动构造和仅复制分配”类型的移动分配? 【发布时间】:2019-08-09 07:42:36 【问题描述】:

标准要求optional的移动赋值运算符...

constexpr optional& operator=( optional&& other )

[...] 不应参与重载决议,除非 is_move_constructible_v<T> 为真,is_move_assignable_v<T> 为真。

可选值lhs = rhs; 的赋值也可以

销毁lhs(如果bool(lhs) && !bool(rhs)) 从rhs构造lhs(如果!bool(lhs) && bool(rhs))或 将rhs 分配给lhs(如果bool(lhs) && bool(rhs))。

因此,可以选择为optional 的移动分配设置两组先决条件:

    is_move_constructible_v<T> && is_move_assignable_v<T> is_move_constructible_v<T> && is_copy_assignable_v<T>

如果bool(lhs) && bool(rhs),第二种形式可以使用复制赋值,但如果!bool(lhs) && bool(rhs),则移动构造。

对于以下两类类型,我认为当前的一组先决条件存在一个相当人为的问题:

    虽然构造是赋值操作的一部分,但不可移动赋值但可复制赋值、可移动构造和可复制构造的类型不能从赋值时的移动构造中受益。 optional 复制赋值运算符将被选中并复制构造或复制赋值。

    一个既不能复制构造也不能移动赋值但移动构造和复制赋值的类型根本不能赋值。

这是在 optional 的标准化过程中考虑过的,还是有什么理由不考虑或放弃它?

(免责声明:我知道如果is_copy_assignable 为真,则is_move_assignable 通常为真,除非明确删除移动赋值运算符。)

【问题讨论】:

is_move_constructible_v<T> && is_move_assignable_v<T> == is_move_constructible_v<T> && is_copy_assignable_v<T> 所以不需要。如here 所示,只要不删除复制赋值运算符,隐式删除的移动赋值运算符仍然可以移动赋值。 为什么要明确删除移动操作却允许复制? 如果问题真的是人为的,正如你承认的那样,那么标准通过强制他们有时使用复制赋值运算符而不是增加库实现者所需的工作量是没有意义的移动赋值运算符。或者,就此而言,通过编纂此要求使标准本身复杂化。 如果你有一个可以复制construct,assign但不能移动construct,assign的类型,你在做一些非常奇怪的事情,我不确定它是不是值得让库复杂化以支持这样的事情。 作为一般规则,图书馆不会关心可复制但不可移动的憎恶。 【参考方案1】:

您应该考虑将std::optional::emplace 用于可移动构造类型(第二种情况)

【讨论】:

以上是关于为啥 std::optional 不允许“移动构造和仅复制分配”类型的移动分配?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 std::optional 构造函数使用 std::in_place?

为啥 std::optional::value() &&;返回 &&?

为啥 const rvalue 限定 std::optional::value() 返回 const rvalue 引用?

为啥 std::optional 对 std::nullopt 类型的操作数有一个特殊的相等运算符

为啥 std::optional<int> 的构造比 std::pair<int, bool> 更昂贵?

为啥 std::optional operator* 没有 has_value() 的调试模式断言?