20

In C++ there are few compelling reasons to use a C array over std::vector. One of those few compelling reasons, at least with C++03, was the fact that it is impossible to use a vector to allocate an uninitialized array of objects. The "fill" constructor for std::vector is:

vector(size_type count, const T& value = T())

Meaning that...

int* array = new array[1000000]; 

is likely to be much more efficient than:

std::vector<int> v(1000000); 

...since the vector constructor will have to zero-initialize the array of integers. Thus, when working with a vector of PODs, there is no real equivalent to malloc; the best you can get is an equivalent to calloc.

C++11 seems to have changed this, with the concept of "value-initialization." In C++11, std::vector has a new constructor which takes a single size_type value, with no default argument. This "value-initializes" all elements in the vector. The C++11 standard distinguishes between "value-initialization" and "zero-initialization."

My understanding is that "value-initialization" is equivalent to calling the default constructor on T. If T is a POD type like int, then the default constructor simply creates an uninitialized integer. Thus, in C++11, explicit vector::vector(size_type count) is truly equivalent to malloc if T is a POD.

However, my understanding of this is based on the draft C++11 standard, rather than the final standard.

Question: Is my understanding correct here? Does explicit vector::vector(size_type count) provide an uninitialized array (similar to malloc) if T is a POD?

13
  • 7
    Value initialization means zero initialization for built-in types. Commented Feb 26, 2013 at 19:40
  • 5
    If you want uninitialized storage then use vector::reserve, as always. Commented Feb 26, 2013 at 19:41
  • 1
    @Channel: C++03 also had value initialization as opposed to default initialization and zero initialization. The only relevant bit that changed was std::vector itself. Commented Feb 26, 2013 at 19:42
  • 3
    What's wrong with reserve() and then push_back()? Commented Feb 26, 2013 at 19:46
  • 1
    @MooingDuck: Sorry, aligned_storage. Commented Feb 26, 2013 at 19:50

2 Answers 2

26

Question: Is my understanding correct here? Does explicit vector::vector(size_type count) provide an uninitialized array (similar to malloc) if T is a POD?

No. There is a difference here between C++03 and C++11, but that isn't it. The difference is that in C++03, vector<T>(N) would default construct a T, and then make N copies of it to populate the vector.

Whereas in C++11, vector<T>(N) will populate the vector by default constructing T N times. For POD types the effect is identical. Indeed, I would expect that for almost all types the effect is identical. However for something like a unique_ptr (a move-only type), the difference is critical. The C++03 semantics would never work since you can not make a copy of a move-only type.

So:

vector<unique_ptr<int>> v(10); 

creates a vector of 10 null unique_ptrs (which are not copies of each other).

In the rare case that it makes a difference and you need the C++03 behavior that can easily be accomplished with:

vector<T> v(10, T()); 
Sign up to request clarification or add additional context in comments.

2 Comments

The other potential difference is if it's a vector of a type that acts like a shared resource (so that copies all reference the same underlying resource). If that's default constructible, C++03 would make N copies, all referencing the same underlying resource, while C++11 would create N unrelated objects.
@DaveS: Right, thanks. I've updated the answer with instructions on how to recover this behavior.
10

Note: the value-initialization happens in the allocator, so if you want a vector to do default initialization instead of value initialization for default constructed elements, you can do something like:

template<typename T> struct DefaultInitAllocator { template<typename U> void construct(U* p) { ::new (static_cast<void*>(p)) U; } template<typename U, typename... Args> void construct(U* p, Args&&... args) { ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...); } // ... rest of the allocator interface }; // ... typedef std::vector<int, DefaultInitAllocator<int>> DefaultInitVectorInt; 

7 Comments

seems like just using reserve() is less hassle.
reserve() doesn't let you legally access the space.
when you push_back after reserving, there are no allocations, right?
Right, no allocations (assuming you reserved enough space, of course). But push_back is initializing the back element. Plus, I don't see what push_back() has to do with the OP question. If you want an array that you are, for example, about to Posix read() into it, you don't want to 0-fill it before doing so, which the default allocator will do.
@jiggunjer - Push_back only solves the problem if you do not need to initialize elements in an arbitrary order. It also assumes one is free to change the code that uses the vector.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.