53

Take the following code:

std::vector<int> a;
a.reserve(65536);
std::vector<int> b(a);  //NOTE: b is constructed from a

a.reserve(65536); // no reallocation
b.reserve(65536);

Is capacity copied? Will there be a reallocation on the last line? Does the standard say anything about this or is it silent?

Mike Torrettinni
  • 1,816
  • 2
  • 17
  • 47
user6426033
  • 411
  • 3
  • 5
  • In vector c-tors documentation, it's stated that it copies elements. http://en.cppreference.com/w/cpp/container/vector/vector – paweldac Jun 05 '16 at 07:26
  • Possible duplicate of [std::vector capacity after copying](https://stackoverflow.com/q/2663170/608639) – jww Oct 14 '18 at 03:09

5 Answers5

43

Is capacity copied?

In practice, no. I tested it online in Clang and GCC as well as MSVC and none of them copy the capacity.

Will there be a reallocation on the last line?

If the capacity is less than the argument to reserve (i.e. it doesn't get copied) then yes.

Does the standard say anything about this or is it silent?

No definitions for the copy constructor are provided in vector.cons. Instead we have to look at the container.requirements

X denotes a container class containing objects of type T, a and b denote values of type X, u denotes an identifier, r denotes a non-const value of type X, and rv denotes a non-const rvalue of type X.

X u(a)

X u = a;

Requires: T is CopyInsertable into X (see below).

post: u == a

Now what does it mean for two containers to be equal?

a == b

== is an equivalence relation. equal(a.begin(), a.end(), b.begin(), b.end())

In other words, since it doesn't require capacity to be equal in the comparison, then there's no reason to copy the capacity.

4

Standard says nothing about preserving capacity when you call copy constructor. So you have no any guarantees about it.

But you can do the following trick, which swap a's and b's state, if you need preserving capacity in the copy only:

 std::vector<int> a;
 a.reserve(65536);
 std::vector<int> b(a);
 b.swap(a); // now b has a's state
 assert(b.capacity() == 65536); 
AnatolyS
  • 4,249
  • 18
  • 28
  • This just swaps *which* `reserve` call will cause a reallocation. – Cheers and hth. - Alf Jun 05 '16 at 08:42
  • swap do nothing with allocator, swap only state of objects. – AnatolyS Jun 05 '16 at 10:05
  • So can you swap vectors having different allocators? If so, it can't just swap the single chunk of allocated memory. – JDługosz Jun 05 '16 at 10:22
  • @JDługosz Two vector with different allocator types are different vector types, so you cannot swap them. – AnatolyS Jun 05 '16 at 10:36
  • So "do nothing with allocators" doesn't mean much since they are already equal. But, the type of allocator then allows for *values* of allocator to be specified per instance, and the objects are the same type. – JDługosz Jun 05 '16 at 10:41
  • @JDługosz the swap swaps states only without reallocation. Swap can be applied to objects have the same specialization of vector types. What is the problem? – AnatolyS Jun 05 '16 at 11:27
  • @AnatolyS: I'm not sure if "swap do nothing with allocator, swap only state of objects" was in response to me. If it was then I fail to understand what you were thinking. The two `reserve` calls I referred to are the ones in the OP's example. – Cheers and hth. - Alf Jun 05 '16 at 12:09
  • 2
    @AnatolyS This is not true in C++11. In C++03 allocators where required to be stateless, so the statement "swap does nothing with allocators" is somewhat true. In C++11 the behavior of `swap` regarding to the allocator is customized by `allocator_traits::propagate_on_container_swap`. – sbabbi Jun 05 '16 at 12:10
4

No, capacity is not guaranteed to be preserved by a vector copy construction.

You can do that as follows:

vector<int> b;
b.reserve( a.capacity() );
b = a;

Better encapsulated in a function.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
1

Well, a simple check like the following reveals that the capacity is not copied:

std::vector<int> a;
a.reserve(65536);
cout << "a.capacity is " << a.capacity() << endl; // prints 65536

std::vector<int> b(a);  //NOTE: b is constructed from a
cout << "b.capacity is " << b.capacity() << endl; // prints 0

I believe that upon copying vector a to b, the capacity of b is set to the size of a in most compilers; though this is not guaranteed.

Eissa N.
  • 1,695
  • 11
  • 18
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
  • 700
  • 2
  • 8
  • 17