1

Andrzej 的移动构造函数文章的评论中,我发布了一个被移动的对象可以调用任何没有前置条件的成员函数。我把这个例子std::vector::front作为一个函数给出,你不能在 move-from 上调用它,std::vector因为它具有向量不为空的先决条件。我给出了std::vector::empty,std::vector::push_backstd::vector::reserveas 的示例,您可以(但不应该)在 move-from 上调用std::vector它们,因为它们没有先决条件。

然而,这让我开始思考。std::vector::push_back要求主机系统上有足够的连续内存可用。这与其说是对std::vector对象的要求,不如说是关于它正在运行的系统,但在我看来,这仍然是一个先决条件。

移动构造函数使对象处于有效但未指定状态的上下文是什么,它是否适用于潜在的内存不足情况std::vector::push_back?特别是,如果std::vector::push_back在移动之前可以工作,它是否保证在之后工作(忽略其他进程耗尽内存等问题)?

供参考:§ 17.6.3.1

Table 20 — MoveConstructible requirements [moveconstructible]
Expression  Post-condition
T u = rv;   u is equivalent to the value of rv before the construction
T(rv)       T(rv) is equivalent to the value of rv before the construction
rv’s state is unspecified [ Note:rv must still meet the requirements of the library compo-
nent that is using it. The operations listed in those requirements must work as specified
whether rv has been moved from or not. — end note ]
4

2 回答 2

3

如果内存不足,push_back则以std::bad_alloc异常退出并且后置条件不适用。vector对象保持其原始状态。

当向量达到容量时,会发生以下情况:

  1. 分配一个更大的块。如果这引发bad_alloc异常,请将其传递给用户。没有任何东西被修改过。
  2. 如果类型不可移动,或者可移动但移动构造函数可能会抛出异常,则将序列的元素复制到更大的块中。如果构造函数抛出异常,则销毁更大块中的所有内容,然后释放它,然后重新抛出异常。
  3. 如果类型是可移动的并且移动构造函数是noexcept,则将元素移动到更大的块中。这必须成功。如果noexcept违反了规范,则实施不必尝试将事情移回(这可能并且很可能也会失败。)
  4. 销毁原来的内存块,保留新的内存块。

“有效但未指定”在这种情况下没有任何更深的含义。用户根本不应该对其中的内容做出假设。但是检查它以发现内容,或者忽略内容并添加更多内容,都可以。

从逻辑上讲,任何对象都应该留空或保持其原始状态。我似乎记得vector实际上指定为空,许多程序员都这样认为,无论正确与否。

于 2013-01-05T07:37:17.187 回答
2

What is the context of move constructors leaving an object in a valid but unspecified state, and does it apply to potential out-of-memory situations with std::vector::push_back?

No, it doesn't apply. Having enough memory to do a push_back is not a precondition. It's OK to call push_back when the system has no more memory (in general it's not possible for the program to know in advance if the allocation will succeed or not) and if there isn't enough memory you get an exception. That's just the normal behaviour of push_back not a precondition violation.

In particular, if std::vector::push_back would have worked prior to a move, is it guaranteed to work after (ignoring issues such as other processes using up memory)?

It's legal to attempt to push_back but it's not guaranteed to work. After a move the vector's storage might have been moved to the target of the move, and push_back could cause a reallocation which could fail and throw bad_alloc.

But ignoring allocation failure, the push_back will succeed, and as Howard's comment says, now your vector which previously had an unknown number of elements has an unknown number plus one. Valid but not very useful.

于 2013-01-06T14:55:00.750 回答