14
  • Does vector::operator= change vector capacity? If so, how?
  • Does vector's copy constructor copy capacity?

I looked through documentation but could not find a specific answer. Is it implementation dependent?

Anycorn
  • 46,748
  • 41
  • 153
  • 250
  • 1
    seems like a duplicate of [Is capacity copied in a vector?](https://stackoverflow.com/questions/37639386/is-capacity-copied-in-a-vector), which has an answer that cites, and thus has the edge IMO – underscore_d Oct 13 '18 at 23:49

5 Answers5

9

All you're guaranteed is that:

  1. The vector has enough capacity to store its elements. (Obviously.)
  2. The vector won't get a new capacity until it's current capacity is full.*

So how much extra or little an implementation wants to put is up to the implementation. I think most will make capacity match size, when copying, but it cannot lower capacity. (Because of number 2 above; reallocating while there's enough room is not allowed.)

* Mostly. See Charles' comments below.

GManNickG
  • 459,504
  • 50
  • 465
  • 534
  • 1
    Actually, you're not quite guaranteed (2). In current C++ you are guaranteed that no reallocation occurs after a call to `reserve` until an insertion would take the size beyond the value of the previous call to reserve. Before a call to reserve, or after a call to reserve when the size is between the value of the previous call to reserve and the capacity the implementation is allowed to reallocate early if it so chooses. Whether this is desirable, I don't know. – CB Bailey Apr 18 '10 at 18:08
  • 2
    In C++0x the requirement has changed and after a call to `reserve` no reallocation will happen until an insertion moves the size of the container beyond the `capacity` (note, not the size previously passed to `reserve` any more). This means that you can `reserve`, query the `capacity` and know with certainty exactly when the next reallocation will happen. – CB Bailey Apr 18 '10 at 18:10
  • @Charles: Good to know. :) I will mention in the laziest way possible. – GManNickG Apr 18 '10 at 18:14
  • You are guaranteed #2 through "The total number of elements that the vector can hold without requiring reallocation." [23.2.4.2/1 C++03]. (But you're not guaranteed anything about the destination vector's capacity after assignment other than it is no less than before the assignment, and it is of course no less than the new size of the vector.) –  Apr 18 '10 at 18:29
  • 2
    "...without _requiring_ reallocation..." but that's my point. The standard doesn't mandate that a vector shall not reallocate even if it has spare capacity and doesn't strictly need to. It wouldn't break the interface contract to do this unless the client had actually called `reserve`. – CB Bailey Apr 18 '10 at 18:35
  • The only (sensible??) use that I can think of for this is that if it attempts to reallocate early but fails, `capacity()` would still tell you how many items you can expect to store in the container even if it was only giving you an upper bound on the number of items you can insert before it next attempts reallocation. – CB Bailey Apr 18 '10 at 18:41
  • @Roger Pate: Yes, it's good that they fixed the two calls to `reserve` issue, but note that when 23.2.4.3 hasn't changed. "Causes reallocation if the new size is greater than the old capacity" is still there and still doesn't mean "Reallocation is not caused if the new size is less than or equal to the capacity". To guarantee this behaviour you have to call `reserve` at least once. You can `reserve(0)` in C++0x, in C++0x you would need to `reserve(capacity())` whenever you cause the capacity to change. – CB Bailey Apr 18 '10 at 19:06
  • @Charles: 23.2.4.3/1 hasn't changed because it doesn't need to; 23.1/11 states "Unless otherwise specified .., invoking a container member function .. shall not invalidate iterators to .. that container." –  Apr 18 '10 at 19:25
  • 1
    Someone was confused by what I was pointing out, so explicitly: 23.2.4.3/1 specifies when reallocation occurs, if reallocation does not occur under that (or another) requirement, then it *must not* occur, per 23.1/11. –  Apr 18 '10 at 19:32
  • @Roger: I think you've read a little more into the wording than is actually there. 23.2.4.3 clearly indicates that `insert` (and functions that call `insert`) may cause reallocation and specifies a condition where it must cause reallocation. 23.2.4.2 also places restrictions on reallocation. I don't believe (and clearly the submitter of DR329 also agrees) that it's disallowed to reallocate when the new size is less than the old capacity. – CB Bailey Apr 18 '10 at 19:55
  • 1
    @Charles: You were right that DR329 itself was off the mark (which is why I've deleted that comment), but I respectfully say I believe it's the reverse and you're reading more into it. 23.1/11 says invalidation (which reallocation requires) must not occur unless specified, so where is it specified that op= reallocates if the new size is not more than the old capacity? –  Apr 18 '10 at 20:01
  • @Roger: I hope that the standard doesn't state the op= reallocates if new size <= old capacity at all, as it may be required to not reallocate! – CB Bailey Apr 18 '10 at 20:07
  • @Roger: Please note that 23.1/11 doesn't forbid re-allocation. It says "must not invalidate iterators to, or change the values of,...". Unlike the requirements for swap given above, it explicitly omits "references to". Of course, if vector does reallocate in a function that isn't explicitly allowed to invalidate iterators then it can't use raw pointers as iterators. – CB Bailey Apr 18 '10 at 20:18
  • 1
    @Charles: "Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence." [23.2.4.2/5] I do not interpret that as "Reallocations performed by reserve() invalidate..", so any reallocation must invalidate everything listed. –  Apr 18 '10 at 20:38
  • @Roger: All I'm saying is that vector can move things around any time it chooses, even given 23.1/11, so long as it keeps iterators valid. When it's allowed a "reallocation" it can invalidate everything. It doesn't have to as the client can't rely on it or they will get UB. Just reading 23.2.4.3/1 again, I can't read that first sentence in any way other than leaving the door open for the converse not to be true. Otherwise I'd expect to see "and only if". `reserve` will give you that guarantee if you call it. – CB Bailey Apr 18 '10 at 20:59
  • 1
    @Charles, To me the issue is clear now: Since it's not specified that reallocation happens if the new size is between the old argument of reserve and capacity(), iterators must not be invalidated. Since that happens when reallocation happens, reallocation is forbidden. It's only specified to reallocate if the new size is greater than the capacity. – Johannes Schaub - litb Apr 18 '10 at 21:16
  • @litb: Are you relying on 23.1/11 for the conclusion that iterators must not be invalidated? If so, I'm not convinced but I'll agree to disagree. I think that the section on insert explicitly allows for reallocation (assuming no reserve) even if the new size is less then the original capacity - it's just not prohibited. If you take this as true - and I admit this is probably the cause for our disagreement - then you can't use 23.1/11 to say otherwise as it would be a circular argument. – CB Bailey Apr 18 '10 at 21:43
2

Does vector::operator= change vector capacity? If so, how?

It might change capacity. This happens only if the previous capacity was too small to hold the new size. If so, the new capacity is at least equal to the new size, but could be a larger value.

Does copy constructor copy capacity?

Per Table 65 Container requirements in C++03, X u (a); and X u = a; are both equivalent to X u; u = a;. This makes the copy ctor identical to the op= case, after default constructing the vector.

1

As i wrote before, the copy need not - and usually DOES NOT - retain the capacity of the original vector.

gcc version 4.1.1

$ cat vt.cpp
#include <vector>
#include <iostream>
int main() {
   std::vector<int> v1;
   v1.reserve(50000);
   std::vector<int> v2 = v1;
   std::cout << v1.capacity() << std::endl;
   std::cout << v2.capacity() << std::endl;
   return 0;
}

$ g++ vt.cpp -o vt && ./vt
50000
0

$ cat v2.cpp
#include <vector>
#include <iostream>
int main() {
   std::vector<int> v1;
   v1.reserve(50000);
   std::vector<int> v2;
   v2 = v1;
   std::cout << v1.capacity() << std::endl;
   std::cout << v2.capacity() << std::endl;
   return 0;
}

$ g++ v2.cpp -o v2 && ./v2
50000
0
vectros
  • 19
  • 2
  • That's not using `operator=`. – Johannes Schaub - litb Apr 18 '10 at 18:01
  • 1
    @vectros: If you had `std::vector v2; v2.reserve(v1.size()*2); v2 = v1;`, then it is not allowed to shrink it's capacity while copying the contents of `v1`. – GManNickG Apr 18 '10 at 18:05
  • as for not using operator=() - i realize it was using the copy ctor. it's not relevant. it behaves the same with the copy ctor as it does with operator=(). – vectros Apr 18 '10 at 18:11
  • 1
    they wrote `v2.reserve(size_t)` , rather than `v1.reserve` – Anycorn Apr 18 '10 at 18:13
  • okay, if you explicitly reserved the capacity in v2, v2 will respect that. but that was not the original question. – vectros Apr 18 '10 at 18:15
  • 1
    @vectros: How can both of us be wrong if you say litb is right? :) And the left-hand side of the assignment is exactly what the question is about. The right-hand side of the copy/assignment is trivially unchanged, as both the copy-constructor and assignment-operator have a `const&` parameter. Also, you're falling into the trap of using a specific implementation to point something out about the language (and it's libraries) guarantees. A certain implementation doesn't define the language, the standard does. (That said, I mostly trust gcc. :P) – GManNickG Apr 18 '10 at 18:18
  • i thought you wrote v1.reserve(). not sure why you introduced v2.reserve() into this discussion, as it was not part of the original question. anyway, the code above answers the question. – vectros Apr 18 '10 at 18:22
  • 1
    @vectros: Sure it was part of the question. The question has to do with capacity, and reserve deals directly with that. – GManNickG Apr 18 '10 at 18:41
0
  1. As SGI STL vector soruce code shows below, operator= will reserve space for exactly n elements, i.e. _M_end_of_storage = _M_start + __xlen;.
    template <class _Tp, class _Alloc>
    vector<_Tp,_Alloc>&
    vector<_Tp,_Alloc>::operator=(const vector<_Tp, _Alloc>& __x)
    {
      if (&__x != this) {
        const size_type __xlen = __x.size();
        if (__xlen > capacity()) {
          iterator __tmp = _M_allocate_and_copy(__xlen, __x.begin(), __x.end());
          destroy(_M_start, _M_finish);
          _M_deallocate(_M_start, _M_end_of_storage - _M_start);
          _M_start = __tmp;
          _M_end_of_storage = _M_start + __xlen;
        }
        else if (size() >= __xlen) {
          iterator __i = copy(__x.begin(), __x.end(), begin());
          destroy(__i, _M_finish);
        }
        else {
          copy(__x.begin(), __x.begin() + size(), _M_start);
          uninitialized_copy(__x.begin() + size(), __x.end(), _M_finish);
        }
        _M_finish = _M_start + __xlen;
      }
      return *this;
    }
  1. As SGI STL vector soruce code shows below, vector's copy constructor will reserve space for exactly n elements, i.e. _M_end_of_storage = _M_start + __n;.
      template <class _InputIterator>
      vector(_InputIterator __first, _InputIterator __last,
             const allocator_type& __a = allocator_type()) : _Base(__a) {
        typedef typename _Is_integer<_InputIterator>::_Integral _Integral;
        _M_initialize_aux(__first, __last, _Integral());
      }

      template <class _Integer>
      void _M_initialize_aux(_Integer __n, _Integer __value, __true_type) {
        _M_start = _M_allocate(__n);
        _M_end_of_storage = _M_start + __n;
        _M_finish = uninitialized_fill_n(_M_start, __n, __value);
      }
Terry
  • 560
  • 1
  • 7
  • 14
-2

It is implementation dependent. Most in practice shrink the vectors to the minimum size.

vectros
  • 19
  • 2
  • that seems wrong, because it would invalidate previous `reserve ()` – Anycorn Apr 18 '10 at 17:33
  • 1
    @aaa the standard just says "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 size specified in the most recent call to reserve()" . Assignment of another vector isn't an insertion, so a reallocation may happen during an assignment, which however must rellocate to the same, or to a higher `capacity` than what you specified previously (which makes sense, since the vector assigned from may contain more elements). – Johannes Schaub - litb Apr 18 '10 at 17:36
  • the capacity of the vector being copied remains the same. the vector capacity of the copy is implementation dependent, and its capacity is usually shrunk to the minimum capacity. – vectros Apr 18 '10 at 17:36
  • 2
    However, it's not allowed to shrink. In fact, that's why the `vector().swap(v);` idiom exists. – Johannes Schaub - litb Apr 18 '10 at 17:37
  • to clarify, is your comment regarding assignment or copy constructor? @litb I meant invalidate in sense make smaller. I get what you saying – Anycorn Apr 18 '10 at 17:40
  • swap is a special case. swap is not a copy. copied vectors can have any capacity, as it is not explicitly mandated by the standard. – vectros Apr 18 '10 at 17:41