Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reserve a vector of reference_wrapper objects, how it is possible?

std::reference_wrapper isn't default constructable. So I can't write any of:

std::vector<std::reference_wrapper<int>> vec(10);
std::array<std::reference_wrapper<int>, 3> arr;

However, and to my surprise, you can invoke the std::vector::reserve member function of a vector of std::reference_wrapper objects and effectively change its capacity.

std::vector<std::reference_wrapper<int>> vec;  
vec.reserve(10);

Live Demo

How is this possible since std::reference_wrapper has no default constructor?

like image 450
101010 Avatar asked May 18 '16 14:05

101010


People also ask

Does Reserve change vector size?

reserve() does not change the size of the vector. If new_cap is greater than capacity(), all iterators, including the past-the-end iterator, and all references to the elements are invalidated. Otherwise, no iterators or references are invalidated.

What is a reference_ wrapper?

std::reference_wrapper is a class template that wraps a reference in a copyable, assignable object. It is frequently used as a mechanism to store references inside standard containers (like std::vector) which cannot normally hold references.

Does vector Reserve allocate memory?

vector::reserve does allocate memory, so your question about reserving memory without allocating is incorrect. The point is that reserving memory can be done without changing the vectors size. Basically a vector has two sizes, it's size and it's capacity.


3 Answers

reserve only allocates uninitialized block of memory enough to fit N units without causing reallocation when pushing N new elements.

reserve doesn't build any new elements. Worst case it only moves/copies already created ones.

like image 197
user3104201 Avatar answered Nov 02 '22 10:11

user3104201


You can refer to SGI STL implementation

In stl_vector.h:

void reserve(size_type __n) {
    if (capacity() < __n) {
      const size_type __old_size = size();
      iterator __tmp = _M_allocate_and_copy(__n, _M_start, _M_finish);
      destroy(_M_start, _M_finish);
      _M_deallocate(_M_start, _M_end_of_storage - _M_start);
      _M_start = __tmp;
      _M_finish = __tmp + __old_size;
      _M_end_of_storage = _M_start + __n;
    }
  }

void resize(size_type __new_size) { resize(__new_size, _Tp()); }

reserve doesn't create new element, however resize does.

like image 34
Shangtong Zhang Avatar answered Nov 02 '22 09:11

Shangtong Zhang


vector::reserve(N) simply pre-allocates storage capacity for N elements. The vector constructor you are using is the "default fill" constructor - it creates N default-constructor elements and inserts them into the container.

E.g.

vector<T> v;
v.reserve(N);
assert(0 == v.size());
assert(N >= v.capacity());

vs

vector<T> v(N);
assert(N == v.size());
assert(N >= v.capacity());

Per 23.3.6.3.p2 [vector.capacity] of the C++17 working draft, vector::reserve() only requires that T is MoveInsertable into *this. The constructor in question is specified in 23.3.6.2.p3 [vector.cons], and requires that T shall be DefaultInsertable into *this.

reference_wrapper<> is MoveInsertable but not DefaultInsertable (it cannot be default constructed, as you mentioned). For the exact definitions of these concepts, see 23.2.1.p15 [containers.requirements.general].

like image 27
blelbach Avatar answered Nov 02 '22 09:11

blelbach