0

Using reserve() followed by push_back()'s may be faster than resizing the vector and later performing assignments -- as seen in std::vector reserve() and push_back() is faster than resize() and array index, why?.

However, if I make assignments instead of using push_back(), the size of the vector remains 0:

# include <vector>
int main() {

    std::vector<int> x;
    x.reserve(10);
    x[0] = 10, x[1] = 9, x[2] = 8, x[3] = 7, x[4] = 6;
    x[5] = 5,  x[6] = 4, x[7] = 3, x[8] = 2, x[9] = 1;

    std::cout << x[2] << std::endl;
    std::cout << "SIZE: " << x.size() << std::endl; // 'size()' is 0

    x.resize(10); // removes former entries, since the vector had 'size() = 0'
    std::cout << x[2] << std::endl;
    std::cout << "SIZE: " << x.size() << std::endl; // 'size()' is 10,
                                                    // but values are gone

}

Output:

8
SIZE: 0
0
SIZE: 10

How could I change the size of a vector, without destroying reserved entries? Of course, I still want to use reserve(), to reduce the cost of allocations -- I know the exact size I need.

Community
  • 1
  • 1
Rubens
  • 14,478
  • 11
  • 63
  • 92
  • Please read documentations: vector::reserve is just reserving capacity for the requested amount - the memory is left unitialized –  Jan 18 '14 at 14:53
  • if you are using c++11, you can do `std::vector x { 10,9,...};` – Gasim Jan 18 '14 at 14:54
  • @DieterLücking The memory is left uninitialized, but it is allocated anyway, isn't it? Or is the allocation still left for `push_back()`? – Rubens Jan 18 '14 at 14:56
  • Also, If you know the exact size you need why don't you use `std::array`? EDIT: According to [this](http://www.cplusplus.com/reference/vector/vector/reserve/), _In all other cases, the function call does not cause a reallocation and the vector capacity is not affected._ – Gasim Jan 18 '14 at 14:56
  • @Gasim I'm writing a small serializer. The user *packed* a vector, and wrote its size in the buffer. I know the exact size during the *unpacking*, but I'm still dealing with `vector`. – Rubens Jan 18 '14 at 14:58
  • Sorry for this many comments, but use [resize](http://www.cplusplus.com/reference/vector/vector/resize/) instead – Gasim Jan 18 '14 at 14:59
  • @Gasim `resize()` is exactly what I'm willing not to use, as it will call the constructor while resizing. Guess I might be better off allocating a `T *entries`, and later moving these entries into the `std::vector& user_vector` I need to fill. – Rubens Jan 18 '14 at 15:03
  • This is all undefined behavior. You're off the grid. – John Dibling Jan 18 '14 at 16:58
  • @JohnDibling I didn't know this code works with undefined behavior. Doesn't `reserve()` allocates memory for the elements I request (in case there isn't enough memory allocated for them already)? What exactly is undefined in the code above? – Rubens Jan 18 '14 at 17:23
  • @Rubens: Well, for starters accessing an element that was only `reserve()`'ed is undefined behavior. Since your entire strategy centers around that, everything else following it is goinng to be problemmatic. – John Dibling Jan 18 '14 at 18:54

1 Answers1

1

When I want to avoid the value-initialization of vector elements, I use an allocator adaptor to remove exactly that behavior:

// Allocator adaptor that interposes construct() calls to
// convert value initialization into default initialization.
template <typename T, typename A=std::allocator<T>>
class default_init_allocator : public A {
  typedef std::allocator_traits<A> a_t;
public:
  template <typename U> struct rebind {
    using other =
      default_init_allocator<
        U, typename a_t::template rebind_alloc<U>
      >;
  };

  using A::A;

  template <typename U>
  void construct(U* ptr) {
    // value-initialization: convert to default-initialization.
    ::new (static_cast<void*>(ptr)) U;
  }
  template <typename U, typename...Args>
  void construct(U* ptr, Args&&... args) {
    // Anything else: pass through to the base allocator's construct().
    a_t::construct(static_cast<A&>(*this),
                   ptr, std::forward<Args>(args)...);
  }
};

Types with trivial default initialization - like int - won't be initialized at all. (Live demo at Coliru)

Casey
  • 41,449
  • 7
  • 95
  • 125
  • +1 Thanks for the support! I guess this is the best alternative for what I want -- just wish it were simpler (: Edit: ah, my bad! I can't change the vector structure, since it's given by the user (I'm writing a simple serializer). – Rubens Jan 18 '14 at 17:33
  • @Rubens If Boost is an option for you, [`boost::vector` optionally supports default initialization](http://www.boost.org/doc/libs/1_55_0b1/doc/html/container/extended_functionality.html#container.extended_functionality.default_initialialization). – Casey Jan 18 '14 at 17:38