向量分配是否使“储备”无效?

时间:2014-06-17 09:52:08

标签: c++ language-lawyer

假设我写了

std::vector<T> littleVector(1);
std::vector<T> bigVector;

bigVector.reserve(100);
bigVector = littleVector;

标准是否表示​​bigVector仍会保留100个元素?或者,如果我要push_back 99个元素,我会经历内存重新分配吗?也许它甚至在STL实现之间也有所不同。

之前曾讨论here,但未提供标准参考资料。

3 个答案:

答案 0 :(得分:19)

不幸的是,标准规定了分配器识别序列容器分配的行为,严格来说确实是不一致的。

我们知道(从表28和23.2.1p7),如果allocator_traits<allocator_type>::propagate_on_container_copy_assignment::valuetrue,则在复制分配时替换分配器。此外,从表96和99中我们发现复制赋值的复杂度线性,而后置条件在操作{{1}上是a = t,即(表96)a == t。从23.2.1p7开始,在复制分配后,如果分配器传播,则distance(a.begin(), a.end()) == distance(t.begin(), t.end()) && equal(a.begin(), a.end(), t.begin())

关于载体容量,23.3.6.3 [vector.capacity] 具有:

  

5 - 备注:重新分配使引用序列中元素的所有引用,指针和迭代器无效。保证在调用a.get_allocator() == t.get_allocator()之后插入期间不会重新分配,直到插入使向量的大小大于reserve()的值为止。

如果我们以library DR341作为阅读标准的指南:

  

然而,在调用reserve()之后,23.3.6.3 [vector.capacity]第5段的措辞阻止了向量的容量减少。这使成语无效,因为防止了swap()减少了容量。 [...]

通过在23.3.6.3中添加段落来解决DR341:

  

capacity()
  7 - 效果:void swap(vector<T,Allocator>& x);的内容和capacity()*this的内容进行交换。
  8 - 复杂性:恒定时间。

结论是,从图书馆委员会的角度来看,如果在23.3.6.3中提到,操作只会修改x。 23.3.6.3中未提及复制分配,因此不会修改capacity()。 (移动分配具有相同的问题,特别是考虑到Library DR2321的建议解决方案。)

显然,这是标准中的缺陷,因为传播不等分配器的复制分配必须导致重新分配,与23.3.6.3p5相矛盾。

我们可以期待并希望这个缺陷得到解决,有利于:

  • 非分配器修改副本分配的非简化capacity();
  • 未指定capacity()分配器修改副本分配;
  • 非分配器传播移动分配的非简化capacity();
  • source-container capacity()关于分配器传播的移动分配。

然而,在目前的情况下,在澄清之前,你最好不要依赖任何特定的行为。幸运的是,有一个简单的解决方法可以保证不会减少capacity()

capacity()

答案 1 :(得分:9)

operator=对标准容器的唯一要求是事后src == dst,如表96(23.2,一般容器要求)中所述。此外,同一个表格指定了operator ==

的含义
distance(lhs.begin(), lhs.end()) == distance(rhs.begin(), rhs.end()) // same size
  && equal(lhs.begin(), lhs.end(), rhs.begin()) // element-wise equivalent

请注意,这并不以任何方式包含容量。该标准的任何其他部分也没有提到超出capacity() >= size()的一般不变量的能力。因此,未指定赋值后的容量值,只要保留分配器要求,容器就可以自由地以任何方式实现赋值。


通常,您会发现实现行为

  • 如果分配器比较相等且dst具有足够的容量,它将保留其旧存储
  • 否则它将为新元素分配足够的存储空间,并且
  • 在任何情况下都不会关心src的容量。

当然,移动任务是另一回事。由于它通常是通过窃取源存储来实现的,因此也将采用容量。

答案 2 :(得分:6)

这取决于分配器特征。

此处摘录自http://en.cppreference.com/w/cpp/container/vector/operator%3D

  

如果std :: allocator_traits :: propagate_on_container_copy_assignment()为true,则目标分配器将替换为源分配器的副本。如果目标和源分配器不比较相等,则使用目标(* this)分配器来释放内存,然后在复制元素之前使用其他分配器来分配它。(自C ++ 11起)

基本上,如果分配器不兼容(如果它们不能释放彼此的内存,则会使用新分配器重新分配内存。

它不应该在向量实现之间,而是在分配器实现之间(这是有意义的)。