当第一个向量重新分配时,另一个向量中的 std::vectors 会重新分配吗?
Will std::vectors inside another vector reallocate when the first vector reallocates?
我有一个向量std::vector<std::vector<ContactPairs>> m_contactPairs;
如果我调用 m_contactPairs.push_back()
或任何其他将调整最外层向量大小的函数,该向量内的元素是否必须重新分配(在这种情况下内部元素是 std::vector<ContactPairs>
),或者将内部向量只是做一个浅拷贝并保持指向它们已经拥有的相同内存?
我正在使用 Visual Studio 2010,它是 C++11 之前的版本,但具有一些作为扩展的功能
在 C++03 中,std::vector
重新分配将 复制 ("deep copy") 每个元素。这意味着对于您的情况,每个向量都将被复制。
在 C++11 或更高版本中,std::vector
重新分配将 移动 每个元素 仅当 元素具有移动 noexcept
.
的构造函数
Visual Studio 2010 缺少 noexcept
支持,因此您仍然可以获得深拷贝。
简答:这取决于您使用的标准和库实现:
- 在 C++98 和 C++03 中没有移动,因此所有内容都将被深度复制,包括重新分配。
- 在 C++11 和 C++14 中,将有一个包含重新分配的深拷贝,或者,如果实现在
std::vector<ContactPairs>
的移动构造函数上提供 noexcept
。
- 在即将推出的 C++17 中,内部向量将被移动并且不执行深拷贝。
推理如下:
内部向量类型 std::vector<ContactPairs>
有一个移动构造函数,根据即将到来的 C++17 标准(从 N4296 开始),它是 noexcept
和 not noexcept
根据 C++11 和 C++14 标准,第 [vector.modifiers] 节。你也可以找到这个here。然而,即使 C++11 和 C++14 兼容的实现也可以指定 noexcept
,因为实现可能提供比标准规定的更强大的保证(请参阅 C++ 标准 17.6.5.12)。但是,许多实现还没有这样做。
执行std::vector<T>::push_back()
需要保证strong exception safety, i. e. if it throws there are no side-effects. (See the C++ standard, section [container.requirements.general] §10 or §11 or here。)
如果你调用push_back()
的vector的新大小超过了它的容量,那么需要为新的位置分配内存,并且需要将元素复制或移动到新地点。如果移动外部向量的元素会失败(没有 noexcept
),则需要复制元素以实现强异常保证。在这种情况下,内部向量的每个副本 确实 需要额外的分配。但是,如果 moving 是 noexcept
,则整个循环中的 moving-in-a-loop 不会抛出并且可以安全地用于实现强异常保证。
使用 noexcept
保证实现 std::vector<T>
移动构造对于 std::vector
来说似乎是一件微不足道的事情。我怀疑,为了一致性起见,标准委员会可能一直在犹豫是否将此保证纳入标准:对于其他基于节点的容器,拥有哨兵节点可能是有益的,即使对于默认构造也需要分配。由于移出的容器在移动后需要有效,因此可能需要为 std::list
移动进行分配,这可能会抛出,例如。因此,std::list
和其他基于节点的标准容器类型的移动构造函数没有 noexcept
保证。
我有一个向量std::vector<std::vector<ContactPairs>> m_contactPairs;
如果我调用 m_contactPairs.push_back()
或任何其他将调整最外层向量大小的函数,该向量内的元素是否必须重新分配(在这种情况下内部元素是 std::vector<ContactPairs>
),或者将内部向量只是做一个浅拷贝并保持指向它们已经拥有的相同内存?
我正在使用 Visual Studio 2010,它是 C++11 之前的版本,但具有一些作为扩展的功能
在 C++03 中,std::vector
重新分配将 复制 ("deep copy") 每个元素。这意味着对于您的情况,每个向量都将被复制。
在 C++11 或更高版本中,std::vector
重新分配将 移动 每个元素 仅当 元素具有移动 noexcept
.
Visual Studio 2010 缺少 noexcept
支持,因此您仍然可以获得深拷贝。
简答:这取决于您使用的标准和库实现:
- 在 C++98 和 C++03 中没有移动,因此所有内容都将被深度复制,包括重新分配。
- 在 C++11 和 C++14 中,将有一个包含重新分配的深拷贝,或者,如果实现在
std::vector<ContactPairs>
的移动构造函数上提供noexcept
。 - 在即将推出的 C++17 中,内部向量将被移动并且不执行深拷贝。
推理如下:
内部向量类型
std::vector<ContactPairs>
有一个移动构造函数,根据即将到来的 C++17 标准(从 N4296 开始),它是noexcept
和 notnoexcept
根据 C++11 和 C++14 标准,第 [vector.modifiers] 节。你也可以找到这个here。然而,即使 C++11 和 C++14 兼容的实现也可以指定noexcept
,因为实现可能提供比标准规定的更强大的保证(请参阅 C++ 标准 17.6.5.12)。但是,许多实现还没有这样做。执行
std::vector<T>::push_back()
需要保证strong exception safety, i. e. if it throws there are no side-effects. (See the C++ standard, section [container.requirements.general] §10 or §11 or here。)如果你调用
push_back()
的vector的新大小超过了它的容量,那么需要为新的位置分配内存,并且需要将元素复制或移动到新地点。如果移动外部向量的元素会失败(没有noexcept
),则需要复制元素以实现强异常保证。在这种情况下,内部向量的每个副本 确实 需要额外的分配。但是,如果 moving 是noexcept
,则整个循环中的 moving-in-a-loop 不会抛出并且可以安全地用于实现强异常保证。
使用 noexcept
保证实现 std::vector<T>
移动构造对于 std::vector
来说似乎是一件微不足道的事情。我怀疑,为了一致性起见,标准委员会可能一直在犹豫是否将此保证纳入标准:对于其他基于节点的容器,拥有哨兵节点可能是有益的,即使对于默认构造也需要分配。由于移出的容器在移动后需要有效,因此可能需要为 std::list
移动进行分配,这可能会抛出,例如。因此,std::list
和其他基于节点的标准容器类型的移动构造函数没有 noexcept
保证。