即使根据容量()仍有未使用的空间,std::vector 能否将其数据移动到 emplace_back()处的另一个地址?

Posted

技术标签:

【中文标题】即使根据容量()仍有未使用的空间,std::vector 能否将其数据移动到 emplace_back()处的另一个地址?【英文标题】:Can std::vector move its data to another address at emplace_back() even though there is still unused space according to capacity()? 【发布时间】:2016-10-01 00:04:27 【问题描述】:

是否保证std::vector 仅在size()==capacity() 并调用push_back()emplace_back() 时移动其数据,或者它也可以这样做吗?

【问题讨论】:

【参考方案1】:

规范有点间接。 capacity 指定为:

size_type capacity() const noexcept;

返回:向量可以容纳而不需要重新分配的元素总数。

第二部分来自reserve

reserve(size_type n);

备注:重新分配使所有引用元素的引用、指针和迭代器失效 序列。 在调用reserve() 后发生的插入期间不会发生重新分配 直到插入会使向量的大小大于capacity() 的值

由此可以得出结论,如果大小小于容量,则插入不会导致重新分配。

如果有空闲容量并且您没有明确调用reserve,则没有一个直接的声明表明向量不会重新分配。但是,[container.requirements.general]中有一个通用的容器要求:

除非另有说明(显式或通过根据其他函数定义函数),调用容器成员函数或将容器作为参数传递给库函数不应使对象的迭代器无效或更改对象的值在那个容器内。

最后,我们来描述一下插入的效果:

[insert/emplace_back/push_back:]

备注:如果新容量大于旧容量,则会导致重新分配。如果没有发生重新分配,则插入点之前的所有迭代器和引用都保持有效。

综上所述:除非另有说明,否则调用成员函数不会使迭代器无效。重新分配使迭代器无效(如上面reserve 的一部分所述),因此除非另有说明,否则调用成员函数,特别是插入,不会重新分配,对于新大小超过的情况给出了这样一个覆盖规范当前容量。

【讨论】:

看起来像另一个在指定的行为下。 @NathanOliver:其实没关系,只是有点分散。查看更新。 @KerrekSB 谈论必须将答案拼接在一起:) 不幸的是,我不能再次投票给你。感谢您花费时间和精力来充实这一切。 非常感谢您将这些放在一起。我只知道“如果新大小大于旧容量会导致重新分配”部分,单独来看,它不会禁止重新分配。我现在可以优化我编写的容器来利用它。【参考方案2】:

标准的描述不够清楚。

$23.3.6.5 向量修饰符 [vector.modifiers]:

备注:如果新容量大于旧容量,会导致重新分配。如果没有发生重新分配, 插入点之前的所有迭代器和引用都保持有效。

因此,当向std::vector 添加元素时,当新大小大于当前容量时,肯定会发生重新分配,但并不是说即使新大小小于或等于当前容量也不会发生重新分配。无论如何,如果没有发生重新分配,那么所有迭代器和对插入点之前元素的引用必须保持有效,这意味着数据不会被移动。

insert()emplace_back()emplace()push_back() 也是如此。

引用cppreference.com作为参考:

如果新的size() 大于capacity() 则所有迭代器和 引用(包括过去的迭代器)无效。 否则只有过去的迭代器无效。

【讨论】:

@KerrekSB 您是否认为 cppreference 上的描述不准确? (应该修复?) 不,我完全相信其目的是控制双向重新分配的能力。只是措辞略显草率。否则会很疯狂,并且可能有很多真实世界的代码依赖于这种行为。 其实规范是完整的,密不透风的,只是分散在几个部分。请参阅我的更新答案。

以上是关于即使根据容量()仍有未使用的空间,std::vector 能否将其数据移动到 emplace_back()处的另一个地址?的主要内容,如果未能解决你的问题,请参考以下文章

获取 NodeJS 程序退出码

获取 NodeJS 程序退出码

Ubuntu18.04boot空间不足,删除旧内核又显示有未满足的依赖关系?

如何打印 Vec?

如何查看表空间容量

2018.4.9 三周第一次课