我喜欢让我重新评估我认为正确的问题的问题。谢谢!
首先,我认为“容量”是所有容器都有的。原来这是我的第一个错误。它仅适用于 std::vector 和 std::string(和 std::string_view)。
现在,查看您指定的表达式:
std::vector<T>(v).swap(v);
一方面,我们有std::vector<T>(v),它正在复制v,另一方面,我们有一个v(可能是std::vector<T>)与该副本的交换。
让我们看看每个步骤。
复制构造函数
因为std::vector是一个容器,它必须满足一个“容器”的要求。这就是它的复制构造函数的来源。 std::vector 的复制构造函数在 table 64 的 container.requirements 部分中定义,在表达式 X(a) 的行中。该行还指定复杂性必须是线性的。它还说副本的后置条件“确保:a == X(a)”。
为了确定“a == X(a)”的含义,我们进一步向下查看same table,并看到:
== 是等价关系。 equal(a.begin(), a.end(), b.begin(), b.end())
如果我们将以上所有内容结合起来,我们可以很好地近似复制构造函数的工作:以相同的顺序使用来自另一个 std::vector 的等效值填充一个 std::vector。
但为了迂腐,没有要求分配多少内存,或者更确切地说,分配的内存被调用多少次,只要足以满足std::vector<T>(v) == v。
话虽如此,如果任何实施者分配的资金超过最低要求,我会感到惊讶。在 C++ 中,我们喜欢性能,而不是为我们不使用的东西付费。因此,除非有充分的理由让容量更大,否则复制向量的容量将恰好是复制到它的元素数量。因此,它是特定于实现的。
交换
在same table 中,带有a.swap(b) 表达式的行指的是“注释A”。那张纸条上写着:
那些标有“(注 A)”[...] 的条目对于 [...] 标准容器具有恒定的复杂性。
container.requirements 21.2.1.9 中还要求 swap 不会使任何迭代器失效:
对于标准容器类型的容器 a 和 b [...] 的表达式 a.swap(b) 应交换 a 和 b 的值,而不会对单个容器元素调用任何移动、复制或交换操作. [...]在交换之前引用一个容器中的元素的每个迭代器都应在交换之后引用另一个容器中的相同元素。未指定在交换之前具有值 a.end() 的迭代器在交换之后是否具有值 b.end()。
这是非常有趣的好东西!毕竟,没有人喜欢让迭代器失效。 (与 shrink_to_fit 比较,如果它必须重新分配,它可能会使迭代器无效。)
它也塑造了我们对容器的swap 的理解。由于元素上不允许移动/复制/交换,并且迭代器保持有效,这在很大程度上暗示了实现者swap 的目标将接管源向量的内存。 (是的,我知道这似乎很明显,但标准需要付出巨大的努力才能通过拼写所有内容来确保每个人都清楚这一点。)
正如您所提到的,std::vector 具有 swap 的特化,这也要求交换 capacity。特别是,请参阅“矢量”部分 21.3.11.3.12,其中写道:
效果:将*this的内容和capacity()与x的内容交换。
这意味着标准保证std::vector<T>(v) 的capacity 将被交换为v,当你这样做时:
std::vector<T>(v).swap(v);
TL;DR 交换目标的容量强制与交换源相同。但是,由于标准没有明确规定复制构造的 std::vector 的容量是任何特定值,因此它是特定于实现的。