Is this behavior of vector::resize(size_type n) under C++11 and Boost.Container correct?

boost, c++, c++11, vector

Solution

Not an answer, but a lengthy addendum to Howard's: I use an allocator adapter that basically works the same as Howard's allocator, but is safer since

- it only interposes on value-initialization and not all initializations,

- it correctly default-initializes.

// 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)
    noexcept(std::is_nothrow_default_constructible<U>::value) {
    ::new(static_cast<void*>(ptr)) U;
  }
  template <typename U, typename...Args>
  void construct(U* ptr, Args&&... args) {
    a_t::construct(static_cast<A&>(*this),
                   ptr, std::forward<Args>(args)...);
  }
};

Problem

I have a C++03 application where `std::vector<T>` types are used throughout as temporary buffers. As such, they often get resized using `std::vector<T>::resize()` to ensure they are large enough to hold the required data before use. The C++03 prototype for this function is actually: ``` void resize(size_type n, value_type val = value_type()); ``` So in actuality when calling `resize()`, the vector is enlarged by adding the appropriate number of copies of `val`. Often, however, I just need to know that the `vector` is large enough to hold the data I need; I don't need it initialized with any value. Copy-constructing the new values is just a waste of time. C++11 comes to the rescue (I thought): in its specification, it splits `resize()` into two overloads: ``` void resize(size_type n); // value initialization void resize(size_type n, const value_type &val); // initialization via copy ``` This fits nicely with the philosophy of C++: only pay for what you want. As I noted, though, my application can't use C++11, so I was happy when I came across the Boost.Container library, which indicates support for this functionality in its documentation. Specifically, `boost::container::vector<T>` actually has three overloads of `resize()`: ``` void resize(size_type n); // value initialization void resize(size_type n, default_init_t); // default initialization void resize(size_type n, const value_type &val); // initialization via copy ``` In order to verify that I understood everything, I whipped up a quick test to verify the behavior of C++11 `std::vector<T>` and `boost::container::vector<T>`: ``` #include <boost/container/vector.hpp> #include <iostream> #include <vector> using namespace std; namespace bc = boost::container; template <typename VecType> void init_vec(VecType &v) { // fill v with values [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] for (size_t i = 0; i < 10; ++i) v.push_back(i); // chop off the end of v, which now should be [1, 2, 3, 4, 5], but the other 5 values // should remain in memory v.resize(5); } template <typename VecType> void print_vec(const char *label, VecType &v) { cout << label << ": "; for (size_t i = 0; i < v.size(); ++i) { cout << v[i] << ' '; } cout << endl; } int main() { // instantiate a vector of each type that we're going to test std::vector<int> std_vec; bc::vector<int> boost_vec; bc::vector<int> boost_vec_default; // fill each vector in the same way init_vec(std_vec); init_vec(boost_vec); init_vec(boost_vec_default); // now resize each vector to 10 elements in ways that *should* avoid reinitializing the new elements std_vec.resize(10); boost_vec.resize(10); boost_vec_default.resize(10, bc::default_init); // print each one out print_vec("std", std_vec); print_vec("boost", boost_vec); print_vec("boost w/default", boost_vec_default); } ``` Compiling this with `g++` 4.8.1 in C++03 mode as follows: ``` g++ vectest.cc ./a.out ``` yields the following output: ``` std: 0 1 2 3 4 0 0 0 0 0 boost: 0 1 2 3 4 0 0 0 0 0 boost w/default: 0 1 2 3 4 5 6 7 8 9 ``` This isn't too surprising. I expect the C++03 `std::vector<T>` to initialize the final 5 elements with zeros. I can even convince myself why `boost::container::vector<T>` is doing the same (I would assume it emulates C++03 behavior in C++03 mode). I only got the effect that I wanted when I specifically ask for default initialization. However, when I rebuilt in C++11 mode as follows: ``` g++ vectest.cc -std=c++11 ./a.out ``` I get these results: ``` std: 0 1 2 3 4 0 0 0 0 0 boost: 0 1 2 3 4 0 0 0 0 0 boost w/default: 0 1 2 3 4 5 6 7 8 9 ``` Exactly the same! Which leads to my question: Am I wrong in thinking that I should see the same results from each of the three tests in this case? This seems to indicate that the `std::vector<T>` interface change hasn't really had any effect, as the 5 elements added in the final call to `resize()` still get initialized with zeros in the first two cases.

Original source