Search code examples
c++constructorinitializationstdarraystd-variant

Do elements in `std::array<int, N>` as a class member get default intialised


Consider:

class x {
    std::array<int, 4> data_;

public:
    x() /*no reference to data_ here*/ {}
};

Do the int elements in data_ get zeroed, or is their value indeterminate?

By extension is that also true in this case:

class x {
    std::variant<std::array<int, 4> /*other stuff here*/> data_;

public:
    x() /*no reference to data here*/ {
        data_.emplace<std::array<int, 4>>(/* no args */);
    }
};

EDIT:

Extension: Is there a way I can get the desired behaviour from the variant (to not initialise the data).

If I pair the two example together I should be able to do:

struct no_init_array {
     std::array<int, 4> array;
     no_init_array() { } //does nothing
};

class x {
    std::variant<no_init_array/*other stuff here*/> data_;

public:
    x() /*no reference to data here*/ {
        //call default ctor of no_init_array
        //which does not init the std::array (I hope)
        data_.emplace<no_init_array>(/* no args */);
    }
};

Solution

  • From the std::array documentation, in the constructor's section, we can read:

    initializes the array following the rules of aggregate initialization (note that default initialization may result in indeterminate values for non-class T)

    emphasis mine

    In your case, you have a std::array<int, 4>. int matches the definition of a non-class type so the default initialization will let the data_ member contents with indeterminate values.

    If you had initialized the data_ member as:

    std::array<int, 4> data_ {}; // Note the braces
    

    The elements would have been value-initialized which would lead to zero-initialization for int elements.


    Edit (from comments):

    std::variant::emplace() forwards its arguments but since you did not have provided any argument for the emplaced std::array<int, 4>, your std::variant will hold a value-initialized std::array<int, 4> so the underlying int elements will be zero-initialized.


    Since you want the second use-case and you want the array contents to remain uninitialized, you can of course do what you suggested:

    struct X
    {
        std::array<int, 4> data_;
    
        X()
        {}
    };
    struct Y
    {
        std::variant<X, /*...*/> data_ {};
    
        Y()
        {
            data_.emplace<X>();
        }
    };
    

    Live example

    But you need to take care that the array contents will not be accidentally accessed before it is later on properly initialized.

    Edit:

    To initialize the std::array afterwards, you should make sure that it is performed through a reference and not a copy of the array (in order to avoid undefined behaviour by copying uninitialized data).

    For example:

    Y y;
    
    //X x = std::get<X>(y); // Wrong
    X & x = std::get<X>(y); // Right
    
    x.data_[0] = 42;
    x.data_[1] = 422;
    x.data_[2] = 442;
    x.data_[3] = 4422;