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.
Question&Answers:
os