10
votes

Question is in the code below, asking whether using value initialization syntax as shown would mean zero-initialized or uninitialized for the individual bit-field members:

struct S { // S is POD
   int a : 3;
   int b : 1;
};

S s1;
S s2{};

s1.a; // uninitialized (ok, we understand this)
s1.b; //  "

s2.a; // zero or junk?
s2.b; //  "

Here is a refresher for bit-fields: https://en.cppreference.com/w/cpp/language/bit_field

Creating zeroing constructors for structs with many bit fields is typically done with an ugly memset in legacy code since repeating the name of each bitfield member with value-init syntax in constructor initializer list yields unmanageable code. This is done even if the struct is a POD for good measure. Would like to eliminate that if possible in C++11 (default-member initialization syntax is not available for bit fields until C++20 unfortunately). Does C++11 guarantee zero-initialization for this with {}-init syntax?

2
Putting back the only useful comment there were, from NathanOliver: half of the answer is here.YSC
@SergeyA if several compilers have same particular behaviour, then it is quite often guaranteed by the standard. That allows the helpful language lawyer to start looking for the rules that guarantee the behaviour first. This is similar to branch prediction. If there are compilers with conflicting behaviour, the helper can start with the guess that the behaviour is not guaranteed. In this case, if the helper does find a rule which guarantee one behaviour, then the helper would know that it's worth considering whether they're interpreting an ambiguous rule.eerorika

2 Answers

10
votes
struct S {
   int a : 3;
   int b : 1;
};

Per [dcl.init.aggr]/1, S is an aggregate.

An aggregate is an array or a class ([class]) with
(1.1) no user-declared or inherited constructors ([class.ctor]),
(1.2) no private or protected non-static data members ([class.access]),
(1.3) no virtual functions ([class.virtual]), and
(1.4) no virtual, private, or protected base classes ([class.mi]).

[ Note: Aggregate initialization does not allow accessing protected and private base class' members or constructors. — end note ]

The rest of [dcl.init.aggr] defines how an aggregate is initialized, and no mention is made about bit-fields; they are thus initialized following the same rules as other aggregate classes.

The meaning of S s{} is defined in [dcl.init.aggr]/5:

For a non-union aggregate, each element that is not an explicitly initialized element is initialized as follows:
...
(5.2) Otherwise, if the element is not a reference, the element is copy-initialized from an empty initializer list ([dcl.init.list]).

So, let's see [dcl.init.list]/3... and it is huge! Checking point after point, we find [dcl.init.list]/3.11:

(3.11) Otherwise, if the initializer list has no elements, the object is value-initialized.

Which, for a scalar type, means zero-initialization :)

Does C++11 guarantee zero-initialization for this with {}-init syntax?

Yes.

3
votes

As discussed in Is Aggregate Initialization of Bit-Fields Allowed? your struct is an aggregate. When you initialize an aggregate with an empty initializer each member of the aggregate is then, per [dcl.init.aggr]/5.2,

Otherwise, if the element is not a reference, the element is copy-initialized from an empty initializer list ([dcl.init.list]).

and the relevant section from [dcl.init.list] is 3.11

Otherwise, if the initializer list has no elements, the object is value-initialized. [ Example:

int** pp {};   // initialized to null pointer

— end example ]

So, each element will be value initialized and the relevant part of value initializing in [dcl.init]/8.4 states

otherwise, the object is zero-initialized.

So we know that each bitfeild will be initialized to zero in s2.