vector分配是否使`reserve`无效?
假设我写了
std::vector<T> littleVector(1); std::vector<T> bigVector; bigVector.reserve(100); bigVector = littleVector;
标准是否说bigVector
仍然有100个元素保留? 或者,如果我要push_back
99个元素,我会经历内存重新分配吗? 也许它在STL实现之间甚至有所不同。
这是以前在这里讨论的,但没有给出标准参考。
不幸的是,这个标准在分配器感知的序列容器分配上没有详细说明行为,事实上严格来说是不一致的。
我们知道(从表28和23.2.1p7),如果allocator_traits<allocator_type>::propagate_on_container_copy_assignment::value
为true
那么分配器在复制分配上被replace。 进一步,从表96和表99中我们发现复制分配的复杂性是线性的 ,并且操作后条件 a = t
是a == t
,即(表96), distance(a.begin(), a.end()) == distance(t.begin(), t.end()) && equal(a.begin(), a.end(), t.begin())
。 从复制分配后的23.2.1p7开始,如果分配器传播,则a.get_allocator() == t.get_allocator()
。
关于媒介能力,23.3.6.3 [vector.capacity]具有:
5 – 备注:重新分配将使所有引用序列中的元素的引用,指针和迭代器失效。 保证在调用
reserve()
之前发生的插入过程中不发生重新分配,直到插入将使vector的大小大于capacity()
的值。
如果我们以图书馆DR341作为阅读标准的指南:
然而,23.3.6.3 [vector.capacity]第5段的措辞阻止了在调用reserve()之后向量的容量减less。 这使成语无效,因为防止了swap()减小容量。 […]
DR341通过在23.3.6.3中添加段落得到解决:
void swap(vector<T,Allocator>& x);
7 – 效果:将*this
的内容和capacity()
与x
的内容和capacity()
交换。
8 – 复杂性:时间不变。
结论是,从图书馆委员会的angular度来看,如果在23.3.6.3中提到的话,操作只能修改capacity()
。 复制分配在23.3.6.3中没有提到,因此不修改capacity()
。 (移动分配有相同的问题,特别是考虑到图书馆DR2321提议的决议。)
显然,这是标准中的一个缺陷,因为复制分配传播不平等的分配者必须导致重新分配,与23.3.6.3p5相矛盾。
我们可以期待并希望这个缺陷得到解决,有利于:
- 非分配器修改拷贝分配的非减less
capacity()
; - 未指定
capacity()
分配器修改副本分配; - 非分配器传播移动分配的非减less
capacity()
; - 分配器传播移动分配上的源容器
capacity()
。
但是,在目前的情况下,直到这个问题得到澄清,你将不会依赖任何特定的行为。 幸运的是,有一个简单的解决方法,保证不会减lesscapacity()
:
bigVector.assign(littleVector.begin(), littleVector.end());
对于标准容器, 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的能力。
当然,转移是一个不同的故事。 由于一般通过窃取源存储来实施,所以容量也会被采用。
这取决于分配器的特点。
以下是http://en.cppreference.com/w/cpp/container/vector/operator%3D的摘录:;
如果std :: allocator_traits :: propagate_on_container_copy_assignment()为true,则目标分配器将被源分配器的副本replace。 如果目标和源分配器不相等,则使用目标(* this)分配器来解除分配内存,然后使用其他分配器在复制元素之前分配它(自C ++ 11以来)
基本上,如果分配器不兼容(如果它们不能释放对方的内存,则使用新的分配器重新分配内存。
在vector实现之间,而不是在分配器实现之间(这是有道理的)。