Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Value initialization: default initialization or zero initialization?

I have templated gray_code class which is meant to store some unsigned integer whose underlying bits are stored in Gray code order. Here it is:

template<typename UnsignedInt>
struct gray_code
{
    static_assert(std::is_unsigned<UnsignedInt>::value,
                  "gray code only supports built-in unsigned integers");

    // Variable containing the gray code
    UnsignedInt value;

    // Default constructor
    constexpr gray_code()
        = default;

    // Construction from UnsignedInt
    constexpr explicit gray_code(UnsignedInt value):
        value( (value >> 1) ^ value )
    {}

    // Other methods...
};

In some generic algorithm, I wrote something like this:

template<typename UnsignedInt>
void foo( /* ... */ )
{
    gray_code<UnsignedInt> bar{};
    // Other stuff...
}

In this piece of code, I expected bar to be zero-intialized and therefore bar.value to be zero-initialized. However, after having struggled with unexpected bugs, it appears that bar.value is initialized with garbage (4606858 to be exact) instead of 0u. That surprised me, so I went to cppreference.com to see what the line above was exactly supposed to do...


From what I can read, the form T object{}; corresponds to value initialization. I found this quote interesting:

In all cases, if the empty pair of braces {} is used and T is an aggregate type, aggregate-initialization is performed instead of value-initialization.

However, gray_code has a user-provided constructor. Therefore it is not an aggregate thus aggregate initialization is not performed. gray_code has no constructor taking an std::initializer_list so list initialization is not performed either. The value-initialized of gray_code should then follow the usual C++14 rules of value initialization:

1) If T is a class type with no default constructor or with a user-provided default constructor or with a deleted default constructor, the object is default-initialized.

2) If T is a class type without a user-provided or deleted default constructor (that is, it may be a class with a defaulted default constructor or with an implicitly-defined one) then the object is zero-initialized and then it is default-initialized if it has a non-trivial default constructor.

3) If T is an array type, each element of the array is value-initialized.

4) Otherwise, the object is zero-initialized.

If I read correctly, gray_code has an explicitly defaulted (not user-provided) default constructor, therefore 1) does not apply. It has a defaulted default constructor, so 2) applies: gray_code is zero-initialized. The defaulted default constructor seems to meet all the requirements of a trivial default constructor, so default initialization should not happen. Let's have a look then at how gray_code is zero-initialized:

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

  • 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.

  • If T is a union type, the first non-static named data member is zero-initialized and all padding is initialized to zero bits.

  • If T is array type, each element is zero-initialized

  • If T is reference type, nothing is done.

gray_code is a non-union class type. Therefore, all of its non-static data members should be initialized which means that value is zero-initialized. value satisfies std::is_unsigned and is therefore a scalar type, which means that it should be initialized with "the integral constant zero implicitly converted to T".

So, if I read correctly all of that, in the function foo above, bar.value should always be initialized with 0 and it should never be initialized with garbage, am I right?

Note: the compiler I compiled my code with is MinGW_w4 GCC 4.9.1 with (POSIX threads and dwarf exceptions) in case that helps. While I sometimes get garbage on my computer, I never managed to get anything else than zero with online compilers.


Update: It seems to be a GCC bug that the error is mine and not that of my compiler. Actually, when writing this question, I assumed for the sake of simplicity that

class foo {
    foo() = default;
};

and

class foo {
    foo();
};

foo::foo() = default;

were equivalent. They are not. Here is the quote from the C++14 standard, section [dcl.fct.def.default]:

A function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration.

In other words, when I got garbage values, my defaulted default constructor was indeed user-provided since it was not explicitly efaulted on its first declaration. Therefore, what happened was not zero initialization but default initialization. Thanks @Columbo again for pointing out the real problem.

like image 297
Morwenn Avatar asked Nov 02 '14 13:11

Morwenn


People also ask

Does default constructor initialize 0?

As default constructor initializes the data members of class to 0.

Why do we initialize variables to zero?

In C programming language, the variables should be declared before a value is assigned to it. In an array, if fewer elements are used than the specified size of the array, then the remaining elements will be set by default to 0. Let us see another example to illustrate this.

Is std :: array zero initialized?

std::array::array For elements of a class type this means that their default constructor is called. For elements of fundamental types, they are left uninitialized (unless the array object has static storage, in which case they are zero-initialized).

Are ints automatically initialized to 0?

Instance variables of numerical type (int, double, etc.) are automatically initialized to zero if you provide no other values; boolean variables are initialized to false; and char variables, to the Unicode character with code number zero.


1 Answers

So, if I read correctly all of that, in the function foo above, bar.value should always be initialized with 0 and it should never be initialized with garbage, am I right?

Yes. Your object is direct-list-initialized. C++14's* [dcl.init.list]/3 specifies that

List-initialization of an object or reference of type T is defined as follows:

  • [… Inapplicable bullet points…]

  • Otherwise, if T is an aggregate, aggregate initialization is performed (8.5.1).

  • Otherwise, if the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.

  • […]

Your class isn't an aggregate since it has user-provided constructors, but it does have a default constructor. [dcl.init]/7:

To value-initialize an object of type T means:

  • if T is a (possibly cv-qualified) class type (Clause 9) with either no default constructor (12.1) or a default constructor that is user-provided or deleted, then the object is default-initialized;

  • if T is a (possibly cv-qualified) class type without a user-provided or deleted default constructor, then the object is zero-initialized and the semantic constraints for default-initialization are checked, and if T has a non-trivial default constructor, the object is default-initialized;

[dcl.fct.def.default]/4:

A special member function is user-provided if it is user-declared and not explicitly defaulted […] on its first declaration.

So your constructor is not user-provided, therefore the object is zero-initialized. (The constructor is not called since its trivial)

And finally, in case this was not clear, to zero-initialize an object or reference of type T means:

  • if T is a scalar type (3.9), the object is initialized to the value obtained by converting the integer literal 0 (zero) to T;

  • if T is a (possibly cv-qualified) non-union class type, each non-static data member and each base-class subobject is zero-initialized and padding is initialized to zero bits;

  • […]


Thus either

  • Your compiler is bugged

  • …or your code triggers undefined behavior at some other point.


* The answer is still yes in C++11, though the quoted sections are not equivalent.

like image 137
Columbo Avatar answered Oct 01 '22 09:10

Columbo