当第一个向量重新分配时,另一个向量中的 std::向量会重新分配吗?

Will std::vectors inside another vector reallocate when the first vector reallocates?

本文关键字:向量 分配 新分配 std 第一个 另一个      更新时间:2023-10-16

我有一个向量std::vector<std::vector<ContactPairs>> m_contactPairs;

如果我调用 m_contactPairs.push_back() 或任何其他将调整最外层向量大小的函数,该向量内的元素是否必须重新分配(本例中的内部元素std::vector<ContactPairs> ),还是内部向量只是做一个浅拷贝并继续指向他们已经拥有的相同内存?

我使用的是Visual Studio 2010,它是C++11之前的,但具有一些扩展功能

简短回答:这取决于您使用的标准和库实现:

  • 在 C++98 和 C++03 中没有移动,因此所有内容都将被深度复制,包括重新分配。
  • 在 C++11 和 C++14 中,将有一个包含重新分配的深层副本,或者,如果实现提供了 std::vector<ContactPairs> 的移动构造函数noexcept
  • 在即将到来的 C++17 中,内部向量将被移动,并且不执行深度复制。
理由

如下:

  1. 内部向量类型 std::vector<ContactPairs> 有一个移动构造函数,该构造函数根据即将推出的 C++17 标准(截至 N4296)noexcept而不是根据 C++11 和 C++14 标准第 [vector.modifiers] 节noexcept。你也可以在这里找到这个。然而,即使是符合C++11和C++14的实现也可以指定noexcept,因为实现可以提供比标准规定的更强的保证(见C++标准17.6.5.12)。不过,许多实施还没有这样做。

  2. std::vector<T>::push_back()的实现需要保证强大的异常安全性,即如果它抛出,则没有副作用。(请参阅C++标准第 [container.requirements.general] §10 或 §11 一节或此处。

  3. 如果调用push_back()的矢量的新大小超过其容量,则需要为新点分配内存,并且需要将元素复制或移动到新点。如果移动外部向量的元素可能失败(无noexcept),则需要复制元素才能实现强异常保证。在这种情况下,内部向量的每个副本都需要额外的分配。但是,如果移动是noexcept,那么整个移动循环不能抛出,并且可以安全地用于实现强异常保证。

noexcept保证的情况下实施std::vector<T>搬迁建设,对std::vector来说似乎是一件微不足道的事情。我怀疑,为了一致性,标准委员会可能犹豫是否要把这个保证放到标准中:对于其他基于节点的容器,拥有哨兵节点可能是有益的,即使对于默认构造也需要分配。由于移自容器在移动后需要有效,因此可能需要为可能引发的std::list移动进行分配。因此,对于std::list和其他基于节点的标准容器类型的移动构造函数,没有noexcept保证。

在 C++03 中,std::vector重新分配将复制("深层复制")每个元素。 这意味着对于您的情况,每个向量都会被复制。

在 C++11 或更高版本中,仅当元素具有noexcept的移动构造函数时,std::vector重新分配才会移动每个元素。

Visual Studio 2010缺乏noexcept支持,因此您仍然可以获得深层副本。