从中移动数据的向量的容量

Capacity of the vector from which data was moved

从中移动数据后,std::vector 的容量必须为零吗?假设源向量和目标向量的内存分配器始终匹配。

std::vector< int > v{1, 2, 3};
assert(0 < v.capacity());
std::vector< int > w;
w = std::move(v);
assert(0 == v.capacity());

Here 表示移动赋值运算符使窃取的 RHS 向量处于有效但未指定的状态。但无处指出,该向量应该在移动分配操作期间执行额外的内存分配。另一个注意事项是:向量具有连续的内存区域作为底层存储。

移出向量的状态未指定,但在移动后有效,如您所见。

这意味着它实际上可以处于任何有效状态,特别是你不能假设它的容量为 0。它可能为零,这很有意义,但事实并非如此全部保证。

但是,实际上,如果您不太关心标准,我想您可以依赖容量为 0。由于移动操作的限制,矢量移动构造函数几乎具有从移动的内存中窃取内存,将其留空。几乎所有 cases/implementations 都会发生这种情况,但这不是必需的。

一个特别扭曲的实现可能会决定在移出的向量中保留一些元素只是为了惹你生气。从技术上讲,这不会违反任何要求。

如果您的问题是关于 vector 的移动构造,答案很简单,移动后源 vector 留空。这是因为

中的要求

Table 99 — Allocator-aware container requirements

Expression:

  X(rv)
  X u(rv)

Requires: move construction of A shall not exit via an exception.

post: u shall have the same elements as rv had before this construction; the value of u.get_allocator() shall be the same as the value of rv.get_allocator() before this construction.

Complexity: constant

(需求子句中的A是分配器类型)

不断的复杂性让我们别无选择,只能从源头 vector 窃取资源,这意味着它处于 有效但未指定的状态 你会需要将其留空,capacity() 将等于零.


在移动分配的情况下,答案要复杂得多。同Table 99 列出移动分配的要求为

Expression:

  a = rv

Return type:

  X&

Requires: If allocator_traits<allocator_type>::propagate_on_container_move_assignment::value is false, T is MoveInsertable into X and MoveAssignable. All existing elements of a are either move assigned to or destroyed.

post: a shall be equal to the value that rv had before this assignment.

Complexity: linear

这里有不同的案例需要评估。


先说allocator_traits<allocator_type>::propagate_on_container_move_assignment::value == true,然后allocator也可以移动赋值。 §23.2.1/8

中提到了这一点

... The allocator may be replaced only via assignment or swap(). Allocator replacement is performed by copy assignment, move assignment, or swapping of the allocator only if allocator_traits<allocator_type>::propagate_on_container_copy_assignment::value, allocator_traits<allocator_type>::propagate_on_container_move_assignment::value, or allocator_traits<allocator_type>::propagate_on_container_swap::value is true within the implementation of the corresponding container operation.

因此目标 vector 将销毁其元素,移动源中的分配器,目标 vector 从源中获取内存缓冲区的所有权。这将使源 vector 为空,并且 capacity() 将等于零 .


现在让我们考虑 allocator_traits<allocator_type>::propagate_on_container_move_assignment::value == false 的情况。这意味着无法将来自源的分配器移动分配给目标 vector。因此,在确定要做什么之前,您需要检查两个分配器是否相等。

如果 dest.get_allocator() == src.get_allocator(),则目标 vector 可以自由地从源获取内存缓冲区的所有权,因为它可以使用自己的分配器来释放存储。

Table 28 — Allocator requirements

Expression:

  a1 == a2

Return type:

  bool

returns true only if storage allocated from each can be deallocated via the other. ...

执行的操作顺序与第一种情况相同,只是源分配器没有移动分配。这将使源 vector 为空,并且 capacity() 将等于零 .


在最后一种情况下,如果allocator_traits<allocator_type>::propagate_on_container_move_assignment::value == falsedest.get_allocator() != src.get_allocator(),则源分配器无法移动,目标分配器无法释放源分配器分配的存储空间,因此不能从源中窃取内存缓冲区。

来自源 vector 的每个元素必须 move inserted or move assigned 到目标 vector。完成哪个操作取决于目标的现有大小和容量 vector

vector 在移动分配后保留其内存缓冲区的所有权,由实现决定是否释放缓冲区,并且 vector 很可能会有 capacity() greater than 0.


为确保您在尝试重新使用已从中移动分配的 vector 时不会 运行 进入未定义的行为,您应该首先调用 clear() 成员函数。这可以安全地完成,因为 vector::clear 没有先决条件,并且 return 和 vector 将进入有效和指定的状态。

此外,vector::capacity 也没有前置条件,因此您始终可以查询从 vector.

移动的 capacity()