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.
C++ Vector Library - resize() FunctionThe C++ function std::vector::resize() changes the size of vector. If n is smaller than current size then extra elements are destroyed. If n is greater than current container size then new elements are inserted at the end of vector.
vector::resizeResizes the container to contain count elements. If the current size is greater than count , the container is reduced to its first count elements.
Vectors are known as dynamic arrays which can change its size automatically when an element is inserted or deleted. This storage is maintained by container.
Resizing a vector doesn't destroy the values stored in the vector (except for those beyond the new size when shrinking, of course), however growing a vector beyond its capacity will copy (or, in C++11, move) them to a new place, thus invalidating and iterators, pointers or references to those elements.
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
// 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)...); } };
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With