移动构造函数的后置条件是什么

What is the post condition of a move constructor?

本文关键字:条件 是什么 构造函数 移动      更新时间:2023-10-16

考虑一个类:

class foo {
public:
  foo(foo &&rhs) { /* some code */ }
  ~foo() noexcept { /* code that does destruction of owning objects */ }
private:
  /* some non-trivial pointer graph like structure */
};

假设:

{
  foo &f = get_from_other_class();
  foo g = std::move(f);
  // some time later f goes our of scope or the owning object is destroyed.
}

执行std::move(f)后,哪些职位条件适用于f

注意

我怀疑f必须仍然是干净的可破坏的(不破坏拥有g的内容(,但我在 C++11 标准中没有找到相应的引用。我正在浏览 12.8 复制和移动类对象。

每个对象都必须是可破坏的,除非你只动态分配它。在被"移出"之后,即将其值绑定到右值引用(例如移动构造函数的第一个参数(,对象仍然需要有效且可破坏,但通常它可以处于"未指定"状态,您根本不应该再查看该对象并让它超出范围或重新分配它。

某些类型提供了更强的保证,例如std::unique_ptr承诺在移动构造时,它将移出对象保留在等于 nullptr 的状态。

右值

和右值引用背后的一般思想是,右值的对象不应具有别名,具体而言,当对象绑定到右值引用时,该对象没有其他别名。标准库对其公开的右值引用接口做出这些假设。

后置条件是移自对象处于未指定但有效的状态。正是如此:它是未指定的,这意味着只要它是有效的,它就可以是你选择它(或库实现者(的任何内容。

在执行 std::move(f( 之后,哪些后置条件适用于 f?

这在很大程度上可以由foo的作者决定,但也受到对foo提出要求的算法(std与否(的约束。

许多算法将要求在其移自状态下foo Destructible并可以为其分配新值。 但这取决于使用foo的上下文。

如果foo与 std 库组件一起使用,则要求由表 20 -- MoveConstructible 要求规定:

RV 的状态未指定 [ 注:RV 仍需满足要求 使用它的库组合。中列出的操作 无论 RV 是否已移动,这些要求必须按照指定的方式工作 从或不从。

例如:假设您用vector<foo>调用std::sortstd::sort需要fooSwappableMoveConstructibleDestructibleMoveAssignableLessThanComparable

std::sort将这些要求放在foo foo是否处于移出状态

严格来说,如果与std::sort一起使用,则在处于移自状态时不需要foo LessThanComparable。 实现者比较移自对象是没有意义的,因为它的值未指定。 尽管如此,C++11和C++14标准目前要求LessThanComparable。 结果不需要是合理的,但要求在执行时不会崩溃。 未来的标准可能会放宽这一要求,但谁知道呢。

因此,总而言之,foo的作者可以说明允许对移自foo执行哪些操作。

任何算法都可以说明它对它所操作的类型的要求。

foo满足算法要求的交叉点中,代码起作用。 C++标准不考虑移出状态特殊。

表 20 (§ 17.6.3.1( 定义了 MoveConstructible 要求:

+------------+----------------------------------------------------------------+|表达式 |                        后置条件 |+------------+----------------------------------------------------------------+|T u = rv; |U相当于建造前房车的价值 ||T(房车( |T(rv( 相当于施工前 rv 的值 |+------------+----------------------------------------------------------------+

rv 的状态未指定 [ 注意:rv 仍必须满足 使用它的库组件的要求。这 这些要求中列出的操作必须按指定工作,无论是否 房车是否已移出。

该注释指出,根据@Kerrek的回答,库组件可能有不同的要求。

移自对象必须准备好运行析构函数。

Per Stroustrup, C++ Programming Language, 4th Edition 2014

如果 x 被移出,x 将具有"一些移出状态"......

"对于最有趣的案例,容器, 移出状态为"空"...

我倾向于认为它类似于默认的初始化变量。

内置类型的值在移动后保持不变。

default moved-from state是默认析构函数和默认复制赋值正常工作的。

其中一个定义是,如果析构函数运行,以前由对象管理的任何资源将不再由对象管理;析构函数将不理会它,或者不知道。我想这是实现/用户依赖的。

简而言之,你可以用x做任何你想做的正确事情,它应该是正确的,就像新变量一样。

因为它是move的,所以,在g = std::move(f);之后,f的语义内容应该失效,g被验证。 move只保留语义内容的一个有效副本。
"invalidate/validate"的语义取决于类,例如,如果f是一个字符串,则应将其设置为长度为0,如果f是线程,则应设置为std::thread()。但是,详细行为取决于类的移动构造函数
当然f仍然是可破坏的。