Does a vector assignment invalidate the `reserve`?

前端 未结 3 1637
天命终不由人
天命终不由人 2021-02-01 15:03

Suppose I write

std::vector littleVector(1);
std::vector bigVector;

bigVector.reserve(100);
bigVector = littleVector;

Does t

相关标签:
3条回答
  • 2021-02-01 15:06

    This depends on the allocator traits.

    Here's an excerpt from 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)

    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).

    0 讨论(0)
  • 2021-02-01 15:18

    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 ==:

    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.


    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,
    • otherwise it will allocate just enough storage for the new elements, and
    • in no case will care what the capacity of src is.

    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.

    0 讨论(0)
  • 2021-02-01 15:21

    Unfortunately, the standard underspecifies behavior on allocator-aware sequence container assignment, and indeed is strictly speaking inconsistent.

    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().

    With regard to vector capacity, 23.3.6.3 [vector.capacity] has:

    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 of capacity().

    If we take library DR341 as a guide to reading the Standard:

    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. [...]

    DR341 was resolved by adding paragraphs into 23.3.6.3:

    void swap(vector<T,Allocator>& x);
    7 - Effects: Exchanges the contents and capacity() of *this with that of x.
    8 - Complexity: Constant time.

    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.)

    Clearly, this is a defect in the Standard, as copy assignment propagating unequal allocators must result in reallocation, contradicting 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());
    
    0 讨论(0)
提交回复
热议问题