Suppose I write
假设我写
std::vector<T> littleVector(1);
std::vector<T> bigVector;
bigVector.reserve(100);
bigVector = littleVector;
Does the standard say that bigVector
will still have 100 elements reserved? Or would I experience memory reallocation if I were to push_back
99 elements? Perhaps it even varies between STL implementations.
标准说bigVector还是保留了100个元素吗?如果我要回推99个元素,我是否会经历内存重新分配?甚至在STL实现之间也存在差异。
This was previously discussed here but no Standard references were given.
这里已经讨论过了,但是没有给出标准的引用。
3 个解决方案
#1
18
Unfortunately, the standard underspecifies behavior on allocator-aware sequence container assignment, and indeed is strictly speaking inconsistent.
不幸的是,标准的标准下指定了对allocator感知的序列容器赋值的行为,并且严格地说,严格来说是不一致的。
We know (from Table 28 and from 23.2.1p7) that if allocator_traits<allocator_type>::propagate_on_container_copy_assignment::value
is true
then the allocator is replaced on copy assignment. Further, from Tables 96 and 99 we find that the complexity of copy assignment is linear, and the post-condition on operation a = t
is that a == t
, i.e. (Table 96) that distance(a.begin(), a.end()) == distance(t.begin(), t.end()) && equal(a.begin(), a.end(), t.begin())
. From 23.2.1p7, after copy assignment, if the allocator propagates, then a.get_allocator() == t.get_allocator()
.
我们知道(从表28和23.2.1p7),如果allocator_traits
With regard to vector capacity, 23.3.6.3 [vector.capacity] has:
关于矢量容量,23.3.6.3[矢量。能力):
5 - Remarks: Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence. It is guaranteed that no reallocation takes place during insertions that happen after a call to
reserve()
until the time when an insertion would make the size of the vector greater than the value ofcapacity()
.5 -备注:重定位使引用序列中的元素的所有引用、指针和迭代器无效。可以保证在调用reserve()之后的插入过程中不会发生重新分配(),直到插入将使vector的大小大于容量()的值。
If we take library DR341 as a guide to reading the Standard:
如果我们以图书馆DR341作为阅读标准的指南:
However, the wording of 23.3.6.3 [vector.capacity]paragraph 5 prevents the capacity of a vector being reduced, following a call to reserve(). This invalidates the idiom, as swap() is thus prevented from reducing the capacity. [...]
然而,23.3.6.3[向量]的措词。第5段防止在调用reserve()之后减少向量的容量。这使得“swap()”被禁止,从而无法减少容量。[…]
DR341 was resolved by adding paragraphs into 23.3.6.3:
DR341通过在23.3.6.3中增加段落来解决:
void swap(vector<T,Allocator>& x);
7 - Effects: Exchanges the contents andcapacity()
of*this
with that ofx
.
8 - Complexity: Constant time.无效的交换(向量< T,分配器> & x);7 -效果:将*的内容和容量()与x的内容和容量()交换。
The conclusion is that from the point of view of the Library committee, operations only modify capacity()
if mentioned under 23.3.6.3. Copy assignment is not mentioned under 23.3.6.3, and thus does not modify capacity()
. (Move assignment has the same issue, especially considering the proposed resolution to Library DR2321.)
结论是,从图书馆委员会的角度来看,只有在23.3.6.3下提到的运行权()才会被修改。23.3.6.3中没有提到复制分配,因此不修改容量()。(搬迁作业也有同样的问题,特别是考虑到针对图书馆DR2321的拟议决议。)
Clearly, this is a defect in the Standard, as copy assignment propagating unequal allocators must result in reallocation, contradicting 23.3.6.3p5.
显然,这是标准中的一个缺陷,因为传播不相等分配器的复制分配必须导致重新分配,这与23.3.6.3p5相矛盾。
We can expect and hope this defect to be resolved in favour of:
我们可以期望并希望这一缺陷得到解决,有利于:
- non-reduced
capacity()
on non-allocator-modifying copy assignment; - 非分配-修改拷贝分配的非减少容量();
- unspecified
capacity()
on allocator-modifying copy assignment; - 分配-修改拷贝分配的未指定能力();
- non-reduced
capacity()
on non-allocator-propagating move assignment; - 非分配-传播移动分配的非减少容量();
- source-container
capacity()
on allocator-propagating move assignment. - 分配或传播移动分配的源容器容量()。
However, in the current situation and until this is clarified you would do well not to depend on any particular behavior. Fortunately, there is a simple workaround that is guaranteed not to reduce capacity()
:
然而,在目前的情况下,在澄清之前,您最好不要依赖于任何特定的行为。幸运的是,有一个简单的解决方案可以保证不会减少容量():
bigVector.assign(littleVector.begin(), littleVector.end());
#2
9
The only requirement on operator=
for standard containers is that afterwards, src == dst
, as specified in Table 96 (in 23.2, General Container Requirements). Furthermore, the same table specifies the meaning of operator ==
:
对于标准容器,操作符=的唯一要求是,src = dst,如表96所示(23.2中,一般容器要求)。此外,同一表指定运算符==的含义:
distance(lhs.begin(), lhs.end()) == distance(rhs.begin(), rhs.end()) // same size
&& equal(lhs.begin(), lhs.end(), rhs.begin()) // element-wise equivalent
Note that this doesn't include capacity in any way. Nor does any other part of the standard mention capacity beyond the general invariant that capacity() >= size()
. The value of capacity after assignment is therefore unspecified, and the container is free to implement assignment whichever way it wants, as long as allocator requirements are kept.
注意,这并不包括容量。除了容量()>= size()之外,标准的任何其他部分都没有提到容量。因此,赋值之后的容量值是未指定的,只要保留分配器需求,容器可以*地以任何方式实现赋值。
In general, you will find that implementations behave such that
通常,您会发现实现的行为是这样的
- if the allocators compare equal and dst has sufficient capacity, it will retain its old storage,
- 如果分配器相等并且dst有足够的容量,它将保留它的旧存储器,
- otherwise it will allocate just enough storage for the new elements, and
- 否则,它将为新元素分配足够的存储空间
- in no case will care what the capacity of src is.
- 在任何情况下都不会关心src的容量是多少。
Of course, move assignment is a different story. Since it is generally implemented by stealing the source storage, the capacity will be taken as well.
当然,搬家任务是另一回事。由于它通常是通过偷取源存储来实现的,所以容量也会被占用。
#3
6
This depends on the allocator traits.
这取决于分配器的特性。
Here's an excerpt from http://en.cppreference.com/w/cpp/container/vector/operator%3D:
以下是http://en.cppreference.com/w/cpp/container/vector/operator%3D的摘录:
If std::allocator_traits::propagate_on_container_copy_assignment() is true, the target allocator is replaced by a copy of the source allocator. If the target and the source allocators do not compare equal, the target (*this) allocator is used to deallocate the memory, then other's allocator is used to allocate it before copying the elements.(since C++11)
如果std::allocator_traits:: ate_on_container_copy_assignment()是正确的,那么目标分配器将被源分配器的副本替代。如果目标和源分配器比较不相等,则使用目标(*this)分配器来释放内存,然后使用其他分配器在复制元素之前分配它。(因为c++ 11)
Basically, the memory is reallocated with the new allocator, if the allocators are incompatible (if they cannot deallocate each-other's memory.
基本上,如果分配器是不兼容的(如果它们不能释放彼此的内存,那么内存将与新的分配器重新分配)。
It shouldn't matter between vector implementations, but between allocator implementations (which makes sense).
在向量实现之间不应该有关系,但是在分配器实现之间(这是有意义的)。
#1
18
Unfortunately, the standard underspecifies behavior on allocator-aware sequence container assignment, and indeed is strictly speaking inconsistent.
不幸的是,标准的标准下指定了对allocator感知的序列容器赋值的行为,并且严格地说,严格来说是不一致的。
We know (from Table 28 and from 23.2.1p7) that if allocator_traits<allocator_type>::propagate_on_container_copy_assignment::value
is true
then the allocator is replaced on copy assignment. Further, from Tables 96 and 99 we find that the complexity of copy assignment is linear, and the post-condition on operation a = t
is that a == t
, i.e. (Table 96) that distance(a.begin(), a.end()) == distance(t.begin(), t.end()) && equal(a.begin(), a.end(), t.begin())
. From 23.2.1p7, after copy assignment, if the allocator propagates, then a.get_allocator() == t.get_allocator()
.
我们知道(从表28和23.2.1p7),如果allocator_traits
With regard to vector capacity, 23.3.6.3 [vector.capacity] has:
关于矢量容量,23.3.6.3[矢量。能力):
5 - Remarks: Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence. It is guaranteed that no reallocation takes place during insertions that happen after a call to
reserve()
until the time when an insertion would make the size of the vector greater than the value ofcapacity()
.5 -备注:重定位使引用序列中的元素的所有引用、指针和迭代器无效。可以保证在调用reserve()之后的插入过程中不会发生重新分配(),直到插入将使vector的大小大于容量()的值。
If we take library DR341 as a guide to reading the Standard:
如果我们以图书馆DR341作为阅读标准的指南:
However, the wording of 23.3.6.3 [vector.capacity]paragraph 5 prevents the capacity of a vector being reduced, following a call to reserve(). This invalidates the idiom, as swap() is thus prevented from reducing the capacity. [...]
然而,23.3.6.3[向量]的措词。第5段防止在调用reserve()之后减少向量的容量。这使得“swap()”被禁止,从而无法减少容量。[…]
DR341 was resolved by adding paragraphs into 23.3.6.3:
DR341通过在23.3.6.3中增加段落来解决:
void swap(vector<T,Allocator>& x);
7 - Effects: Exchanges the contents andcapacity()
of*this
with that ofx
.
8 - Complexity: Constant time.无效的交换(向量< T,分配器> & x);7 -效果:将*的内容和容量()与x的内容和容量()交换。
The conclusion is that from the point of view of the Library committee, operations only modify capacity()
if mentioned under 23.3.6.3. Copy assignment is not mentioned under 23.3.6.3, and thus does not modify capacity()
. (Move assignment has the same issue, especially considering the proposed resolution to Library DR2321.)
结论是,从图书馆委员会的角度来看,只有在23.3.6.3下提到的运行权()才会被修改。23.3.6.3中没有提到复制分配,因此不修改容量()。(搬迁作业也有同样的问题,特别是考虑到针对图书馆DR2321的拟议决议。)
Clearly, this is a defect in the Standard, as copy assignment propagating unequal allocators must result in reallocation, contradicting 23.3.6.3p5.
显然,这是标准中的一个缺陷,因为传播不相等分配器的复制分配必须导致重新分配,这与23.3.6.3p5相矛盾。
We can expect and hope this defect to be resolved in favour of:
我们可以期望并希望这一缺陷得到解决,有利于:
- non-reduced
capacity()
on non-allocator-modifying copy assignment; - 非分配-修改拷贝分配的非减少容量();
- unspecified
capacity()
on allocator-modifying copy assignment; - 分配-修改拷贝分配的未指定能力();
- non-reduced
capacity()
on non-allocator-propagating move assignment; - 非分配-传播移动分配的非减少容量();
- source-container
capacity()
on allocator-propagating move assignment. - 分配或传播移动分配的源容器容量()。
However, in the current situation and until this is clarified you would do well not to depend on any particular behavior. Fortunately, there is a simple workaround that is guaranteed not to reduce capacity()
:
然而,在目前的情况下,在澄清之前,您最好不要依赖于任何特定的行为。幸运的是,有一个简单的解决方案可以保证不会减少容量():
bigVector.assign(littleVector.begin(), littleVector.end());
#2
9
The only requirement on operator=
for standard containers is that afterwards, src == dst
, as specified in Table 96 (in 23.2, General Container Requirements). Furthermore, the same table specifies the meaning of operator ==
:
对于标准容器,操作符=的唯一要求是,src = dst,如表96所示(23.2中,一般容器要求)。此外,同一表指定运算符==的含义:
distance(lhs.begin(), lhs.end()) == distance(rhs.begin(), rhs.end()) // same size
&& equal(lhs.begin(), lhs.end(), rhs.begin()) // element-wise equivalent
Note that this doesn't include capacity in any way. Nor does any other part of the standard mention capacity beyond the general invariant that capacity() >= size()
. The value of capacity after assignment is therefore unspecified, and the container is free to implement assignment whichever way it wants, as long as allocator requirements are kept.
注意,这并不包括容量。除了容量()>= size()之外,标准的任何其他部分都没有提到容量。因此,赋值之后的容量值是未指定的,只要保留分配器需求,容器可以*地以任何方式实现赋值。
In general, you will find that implementations behave such that
通常,您会发现实现的行为是这样的
- if the allocators compare equal and dst has sufficient capacity, it will retain its old storage,
- 如果分配器相等并且dst有足够的容量,它将保留它的旧存储器,
- otherwise it will allocate just enough storage for the new elements, and
- 否则,它将为新元素分配足够的存储空间
- in no case will care what the capacity of src is.
- 在任何情况下都不会关心src的容量是多少。
Of course, move assignment is a different story. Since it is generally implemented by stealing the source storage, the capacity will be taken as well.
当然,搬家任务是另一回事。由于它通常是通过偷取源存储来实现的,所以容量也会被占用。
#3
6
This depends on the allocator traits.
这取决于分配器的特性。
Here's an excerpt from http://en.cppreference.com/w/cpp/container/vector/operator%3D:
以下是http://en.cppreference.com/w/cpp/container/vector/operator%3D的摘录:
If std::allocator_traits::propagate_on_container_copy_assignment() is true, the target allocator is replaced by a copy of the source allocator. If the target and the source allocators do not compare equal, the target (*this) allocator is used to deallocate the memory, then other's allocator is used to allocate it before copying the elements.(since C++11)
如果std::allocator_traits:: ate_on_container_copy_assignment()是正确的,那么目标分配器将被源分配器的副本替代。如果目标和源分配器比较不相等,则使用目标(*this)分配器来释放内存,然后使用其他分配器在复制元素之前分配它。(因为c++ 11)
Basically, the memory is reallocated with the new allocator, if the allocators are incompatible (if they cannot deallocate each-other's memory.
基本上,如果分配器是不兼容的(如果它们不能释放彼此的内存,那么内存将与新的分配器重新分配)。
It shouldn't matter between vector implementations, but between allocator implementations (which makes sense).
在向量实现之间不应该有关系,但是在分配器实现之间(这是有意义的)。