首页 > 解决方案 > 为什么移动 std::optional 不会重置状态

问题描述

我很惊讶地得知移动构造函数(以及相关的赋值)std::optional不会重置可选的移动自,正如在 [19.6.3.1/7] 中看到的那样,它声明“bool(rhs) 未更改”。

这也可以通过以下代码看出:

#include <ios>
#include <iostream>
#include <optional>
#include <utility>

int main() {
  std::optional<int> foo{ 0 };
  std::optional<int> bar{ std::move(foo) };

  std::cout << std::boolalpha
            << foo.has_value() << '\n'  // true
            << bar.has_value() << '\n'; // true
}

这似乎与标准库中移动的其他实例相矛盾,例如std::vector容器移动的位置通常以某种方式重置(在向量的情况下,它保证之后为空)以使其“无效”,即使其中包含的对象也是如此自己已经搬离了。这个决定应该支持这个或潜在的用例是否有任何理由,例如可能试图模仿同一类型的非可选版本的行为?

标签: c++c++17optional

解决方案


除非另有说明,否则类类型的移出对象将处于有效但未指定的状态。不一定是“重置状态”,也绝对不是“无效”。

对于原始类型,移动与复制相同,即源不变。

具有原始成员的类类型的默认移动构造函数将移动每个成员,即保持原始成员不变;用户定义的移动构造函数可能会也可能不会“重置”它们。

移出的向量中可能有也可能没有元素。我们希望它不会,因为这是有效的,但不能依赖它。

由于小字符串优化,一个被移动的std::string对象可能仍然有元素。


moveonstd::optional实际上是由标准(C++17 [optional.ctor]/7)指定的。它被定义为move在包含的类型上执行(如果存在)。它不会将有价值的可选项变成无价值的可选项。

因此,实际上预计您的代码输出true true和实际包含的值foo也应该保持不变。


关于为什么 std::optional's move-constructor 是这样定义的问题:我不能肯定地说;但 anoptional不像最大大小为 1 的向量。它更像是一个附加了有效性标志的变量。optional因此,移动 an就像移动变量是有意义的。

如果将optional旧的左移“空”,a = std::move(b);则将调用托管对象的析构函数b,这将是出乎意料的(至少对我而言)。


推荐阅读