【问题标题】:Does std::vector::push_back have pre-conditions?std::vector::push_back 有前置条件吗?
【发布时间】:2013-01-05 04:53:00
【问题描述】:

comments on Andrzej's move constructor article 中,我发布了一个已移动的对象可以调用任何没有前置条件的成员函数。我给出了示例 std::vector::front 作为一个函数,您不能在已移动的 std::vector 上调用该函数,因为它具有向量不为空的前提条件。我给出了std::vector::emptystd::vector::push_backstd::vector::reserve 的示例,作为您可以(但不应该)调用从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 ]

【问题讨论】:

  • vector 移出的push_back 是合法的,但毫无意义。 vectorpush_back 之前和之后都处于有效但未指定的状态,除非如果成功,您现在知道最后一个元素是什么。
  • 我完全同意你的观点(我在博客上的帖子中强调了一点),我只是从严格的语言角度想知道。

标签: c++ c++11 language-lawyer move-constructor preconditions


【解决方案1】:

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

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

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

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

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

【讨论】:

  • 我不认为vector 被指定为空。对于 GCC 的forward_list,我故意不要在未移动存储的移动分配之后清空右值列表(即分配器不传播或分配器不相等)所以列表的节点不会被释放,并且可以被后续分配重用。对于 GCC 的 vector,它是空的(因为在右值上执行 clear() 只会破坏元素并且不会释放内存),但我认为这不是标准所要求的。
【解决方案2】:

移动构造函数使对象处于有效但未指定状态的上下文是什么,它是否适用于 std::vector::push_back 的潜在内存不足情况?

不,它不适用。有足够的内存来执行push_back 不是先决条件。当系统没有更多内存时调用push_back 是可以的(通常程序不可能提前知道分配是否成功)并且如果没有足够的内存则会出现异常。这只是push_back 的正常行为,而不是违反先决条件。

特别是,如果 std::vector::push_back 在移动之前可以工作,那么它是否保证在移动之后也可以工作(忽略其他进程耗尽内存等问题)?

尝试push_back 是合法的,但不能保证有效。移动后,向量的存储可能已移动到移动的目标,push_back 可能会导致重新分配失败并抛出 bad_alloc

但忽略分配失败,push_back 将成功,正如霍华德的评论所说,现在你以前有未知数量元素的向量有一个未知数加一。有效但不是很有用。

【讨论】:

    猜你喜欢
    • 2013-10-15
    • 2021-06-18
    • 2023-03-21
    • 2015-01-15
    • 2016-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-21
    相关资源
    最近更新 更多