Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Container of struct containing primitive type, zero initialized?

Tags:

c++

c++14

#include <vector>
struct S { int x; };
std::vector<S> v;
int main() { v.resize(1000); return v[42].x; }

Is the above program guaranteed to return 0 in C++14? Why?

like image 900
Andrew Tomazos Avatar asked Feb 06 '17 18:02

Andrew Tomazos


2 Answers

Is the above program guaranteed to return 0 in C++14? Why?

Yes. From [vector.capacity]:

void resize(size_type sz);
13 Effects: If sz < size(), erases the last size() - sz elements from the sequence. Otherwise, appends sz - size() default-inserted elements to the sequence.

Where, from [container.requirements.general]:

An element of X is default-inserted if it is initialized by evaluation of the expression allocator_traits<A>::construct(m, p) where p is the address of the uninitialized storage for the element allocated within X.

construct for std::allocator<T> does, from [default.allocator]:

template <class U, class... Args>
void construct(U* p, Args&&... args);

Effects: ::new((void *)p) U(std::forward<Args>(args)...)

So, that's value-initialization. We're doing new S(), not new S, so the member x will be zero-initialized.


The ways of avoiding this behavior (if desired) are either:

  1. Change the allocator. Provide your own allocator type that has two overloads for construct: one empty (which would do default-initialization) and one taking Args&&....
  2. Change the type. Add a default constructor to S that does no initialization.
like image 59
Barry Avatar answered Sep 23 '22 13:09

Barry


Yes, because std::vector::resize and similar methods perform value initialization by default which in turn value-initializes the members of aggregates:

From cppr:

The effects of value initialization are:
[...]
if T is a class type with a default constructor that is neither user-provided nor deleted (that is, it may be a class with an implicitly-defined or defaulted default constructor), the object is zero-initialized and then it is default-initialized if it has a non-trivial default constructor;

and the Zero Initialiation part does what we need:

If T is an non-union class type, all base classes and non-static data members are zero-initialized, and all padding is initialized to zero bits. The constructors, if any, are ignored.

And of course, zero initialization of our member does the right thing:

If T is a scalar type, the object's initial value is the integral constant zero explicitly converted to T.


The default allocator does, custom allocators may use different initialization. You can use those to leave such values unitialized, see the full article on default-insert.

like image 33
Baum mit Augen Avatar answered Sep 23 '22 13:09

Baum mit Augen