【问题标题】:why swap (this trick) shrink the capacity of vector?为什么交换(这个技巧)缩小向量的容量?
【发布时间】:2019-01-21 21:36:34
【问题描述】:

如果我想缩小向量的容量,一种绑定方式,旧的方法是:

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

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

(在标准中,vector 对swap 的特殊化将交换元素和容量,这是保证。但我找不到任何关于复制初始化容量的保证。如果可能,请引用标准,谢谢!)

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

【问题讨论】:

标签: c++ vector copy-constructor


【解决方案1】:

你是对的。 根本不保证可以工作。只是碰巧可以工作。

但有人可能会问另一个问题:为什么库程序员应该实现一个复制构造函数,它分配的存储空间比所需的多得多? 可能存在舍入问题或一些备用问题,但不太可能例如分配了两倍的存储空间。


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

【讨论】:

  • “为什么库程序员应该实现一个复制构造函数,它分配的存储空间比需要的多得多?”:另一方面,我们可能会问类似的问题:为什么复制构造函数不会' t 做一个“真实”的副本?所以之前的reserve 可以被两个副本使用(对于某种“前缀”向量)。 vector 接口差关于capacity :-/
【解决方案2】:

我喜欢让我重新评估我认为正确的问题的问题。谢谢!

首先,我认为“容量”是所有容器都有的。原来这是我的第一个错误。它仅适用于 std::vectorstd::string(和 std::string_view)。

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

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

一方面,我们有std::vector&lt;T&gt;(v),它正在复制v,另一方面,我们有一个v(可能是std::vector&lt;T&gt;)与该副本的交换。

让我们看看每个步骤。

复制构造函数

因为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&lt;T&gt;(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&lt;T&gt;(v)capacity 将被交换为v,当你这样做时:

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

TL;DR 交换目标的容量强制与交换源相同。但是,由于标准没有明确规定复制构造的 std::vector 的容量是任何特定值,因此它是特定于实现的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-09
    • 2015-10-21
    • 1970-01-01
    相关资源
    最近更新 更多