5

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?

Barry
  • 286,269
  • 29
  • 621
  • 977
101010
  • 41,839
  • 11
  • 94
  • 168
  • 9
    `reserve()` doesn't create objects. What if you did `resize()` instead? – PaulMcKenzie May 18 '16 at 14:34
  • 3
    Almost none of the what you wrote in your question has anything to do with your question... – Barry May 18 '16 at 14:51
  • @Barry feel free to edit then... – 101010 May 18 '16 at 16:03
  • @Barry or propose improvements... – 101010 May 18 '16 at 16:08
  • @101010 Reduced. Feel free to rollback if you dislike the new reading. – Barry May 18 '16 at 16:09
  • @Barry No I trust your judgement as I am a fan of yours and I'm convinced of your solid C++ knowledge, but It would be highly appreciated if you could spare some of your valuable time to explain, briefly of course, the reasons why these wordings aren't related. Thanks either way. – 101010 May 18 '16 at 16:17
  • 1
    @101010 Sure. Your question is really: why can I `reserve` a `vector>`. The first section is about references, but you don't have any references. The second section is explaining what `reference_wrapper` is - but that's also not really relevant since your question would be just as valid for reserving a vector of `struct foo { foo(int ) };` – Barry May 18 '16 at 16:26

3 Answers3

12

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.

erip
  • 16,374
  • 11
  • 66
  • 121
user3104201
  • 356
  • 1
  • 4
  • 7
3

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.

Shangtong Zhang
  • 1,579
  • 1
  • 11
  • 18
  • 3
    Do you have any idea how old the SGI STL is? – uh oh somebody needs a pupper May 18 '16 at 14:52
  • @sleeptightpupper As it's old, it's easy to read, and I believe the basic idea of STL doesn't change much. – Shangtong Zhang May 18 '16 at 14:54
  • 1
    This is only a single (and old) implementation of a specification. I believe a reference to the specification this code implements would be more appropriate (and more generally applicable) here, especially since the odds of OP *using* the implementation you're posting here are low. – jaggedSpire May 18 '16 at 14:54
  • 1
    @jaggedSpire This implementation doesn't implement a the C++ `std`: the C++ `std` was based off of this implementation! While it is not authorative, the fact that it is one of the simplest and easiest to read ways to understand what many `std` types do is a big plus in its column. There may and will be differences, but ease of reading can trump exact correctness in many cases. – Yakk - Adam Nevraumont May 18 '16 at 15:18
  • @Yakk Oh neat! I didn't know that. – jaggedSpire May 18 '16 at 15:20
2

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

blelbach
  • 436
  • 3
  • 8