为什么交换(此技巧)会缩小向量的容量?

时间:2018-08-15 00:31:08

标签: c++ vector copy-constructor

如果我想缩小向量的容量(一种绑定方法),那么做这件事的旧方法是:

std::vector<T>(v).swap(v);

为什么?为什么容量也不能简单地复制呢?在标准中保证,复制初始化将构造一个容量小于要复制的向量的向量吗?我倾向于认为这只是实现的指定。

(在标准情况下,swap的向量专业化将交换元素和容量,这得到了保证。但是我找不到有关复制初始化容量的任何保证。请引用标准如果可能的话,谢谢!)

链接到类似问题:What is the value of the capacity of std::vector when the copy constructor is used?

2 个答案:

答案 0 :(得分:3)

你是对的。 不能保证完全正常工作。它恰好可以正常工作。

但是人们可能会问另一个问题:为什么图书馆程序员应该实现一个复制构造函数,该复制构造函数分配的存储量远远超过了所需的存储量?可能存在舍入问题或一些备用问题,但是这种可能性不大例如分配了两倍的存储空间。


但是,我强烈建议首选新的shrink_to_fit功能。尽管也没有存储保证,但实现可能会使用优化的分配函数,该函数避免复制整个元素(例如,基于realloc)。产生的内存碎片是否带来的弊大于利是另一个问题。但是应该由实施决定。

答案 1 :(得分:1)

我喜欢那些让我重新评估自己认为真实的问题。谢谢!

首先,我以为“容量”是所有容器都具备的功能。原来那是我的第一个错误。仅适用于std::vectorstd::string(以及std::string_view)。

现在,查看您指定的表达式:

std::vector<T>(v).swap(v);

一方面,我们有std::vector<T>(v),它正在复制v,另一方面,我们交换了v(大概是{{ 1}})与该副本。

让我们看看每个步骤。

复制构造器

由于std::vector<T>是一个容器,因此必须满足“容器”的要求。这是其副本构造函数的来源。 std::vector的副本构造函数在table 64的container.requirements节中的表达式为std::vector的行中定义。该行还指定复杂度必须是线性的。它还说副本“ 确保X(a)”的后置条件。

要确定“ a == X(a)”是什么意思,我们在same table中向下看,然后看到:

  

==是等价关系。 a == X(a)

如果我们将以上所有内容综合考虑,将为我们很好地近似于复制构造函数的工作:用相同顺序填充另一个equal(​a.begin(), a.end(), b.begin(), b.end())的等效值来填充std::vector

但是要学究一点,除了足以满足std::vector之外,不需要分配多少内存,也不需要多少次分配内存。

话虽这么说,如果任何实施者分配的资金超过最低要求,我都会感到惊讶。在C ++中,我们喜欢性能,而不是为不使用的东西付费。因此,除非有充分的理由使容量更大,否则复制的向量的容量将恰好是复制到其上的元素的数量。因此,它是特定于实现的。

交换

same table中,带有表达式std::vector<T>(v) == v的行表示“注释A”。那条纸条说:

  

标记为“(注A)”的条目对于标准容器具有恒定的复杂性。

在container.requirements 21.2.1.9中,还要求交换不能使任何迭代器无效:

  

对于标准容器类型[...]的容器a和b,表达式a.swap(b)应当交换a和b的值,而无需对单个容器元素进行任何移动,复制或交换操作。 [...]在交换之前,每个迭代器在一个容器中引用一个元素,在交换之后,每个迭代器应在另一个容器中引用相同的元素。尚未确定在交换之前值为a.end()的迭代器在交换之后是否将值为b.end()。

这是非常有趣的东西!毕竟,没有人喜欢让迭代器无效。 (与a.swap(b)比较,如果必须重新分配迭代器,迭代器可能会失效。)

它也塑造了我们对容器shrink_to_fit的理解。由于不允许在元素上进行移动/复制/交换,并且迭代器保持有效,因此对实现者来说,这swap的目标将大大暗示着源向量将占据内存。 (是的,我知道它看起来很明显,但是该标准花了很大的力气才能通过拼写出所有内容来确保每个人都显而易见。)

正如您所提到的,swap具有std::vector的特殊化,这也要求交换swap。特别是,请参见“向量”部分21.3.11.3.12,其中说:

  

效果:将capacity的内容和capacity()*this的内容和内容交换。

这意味着该标准保证在执行以下操作时,x中的capacity将被交换为std::vector<T>(v)

v

TL; DR强制交换目标的容量与交换源相同。但是,由于该标准并未明确要求复制构造的std::vector<T>(v).swap(v); 的容量为任何特定值,因此它是特定于实现的。