1

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 */);
    }
};
111111
  • 15,686
  • 6
  • 47
  • 62
  • 1
    That depends on when and where and how you define objects of the `x` class. Global? Local? Static? Function argument? Part of another object? – Some programmer dude Feb 26 '20 at 12:43
  • @Someprogrammerdude local function variable is the case I am "concerned" about. The variant case the the one I am most interested in - for the clarity I *want* the data to be uninitialized. – 111111 Feb 26 '20 at 12:55
  • Then the data should be uninitialized. It's really no different from having e.g. a plain `int` member variable, it too will be uninitialized. – Some programmer dude Feb 26 '20 at 12:56
  • 1
    @Someprogrammerdude correct me if I am wrong, and I *hope I am*, but isn't there a difference between `struct x { int i; };` and `struct x { int i{}; }`, and isn't emplace effectively doing the latter? Again, I hope I am wrong here. – 111111 Feb 26 '20 at 13:00
  • There *is* a difference, but that's not what you're doing in the first `x` class shown in the question. If you had e.g. `std::array data_{};` it would be a different thing. – Some programmer dude Feb 26 '20 at 13:07
  • @Someprogrammerdude that seems to be at odds with what others are saying. It seems to me that calling emplace will effectively do: `std::array data_{};` rather than `std::array data_;` if called with no args. Correct me if I am wrong though. – 111111 Feb 26 '20 at 15:41

1 Answers1

3

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;
Fareanor
  • 5,900
  • 2
  • 11
  • 37
  • Thanks, but is that also true for the variant case? Does `variant::emplace` not forward its args onto the element constructor, and thus it is constructed with `{/*Args... expands to nothing*/ }` and therefore would be defaultly initialised? To be clear, I WANT the data to be uninitialised. – 111111 Feb 26 '20 at 12:52
  • @111111 `std::variant::emplace()` does forward its arguments. But you did not provided any argument so I'm not sure to understand you're question. In your example, the emplaced `std::array` in the `std::variant` will follow the above quote for default initialization. – Fareanor Feb 26 '20 at 12:57
  • *"`std::variant` will hold a default-initialized `std::array`"* It should be value-initialized. Since you pass 0 arguments, the array is constructed using `std::array(/*0 args here*/)`, which value-initializes it. – HolyBlackCat Feb 26 '20 at 13:34
  • @HolyBlackCat thanks for the response, is there a work around for what I want, would wrapping the std::array in a struct which inits a default ctor that does nothing achieve that I am after? – 111111 Feb 26 '20 at 14:02
  • @111111 Yes, your first example will do what you want, the array contents will be indeterminate values [as you can see here](https://godbolt.org/z/fZLRxo). – Fareanor Feb 26 '20 at 14:08
  • @Fareanor the first part of the question basically trying to figure out where the behaviour diverges from the latter. It is actually variant that my usecase calls for, and I do *not want* the array to be initialised. Please see my edit - I am wondering if stitching the two examples together gets me the behaviour I need. – 111111 Feb 26 '20 at 14:10
  • @111111 Yes, it works, but keep in mind that it's undefined behaviour to access uninitialized values. I'll edit my answer. – Fareanor Feb 26 '20 at 14:15
  • @Fareanor yeah I know, i am passing the buffer to another function which will initialise them for me in a tight loop on the "critical path". I don't want to initialise them twice unnecessarily. – 111111 Feb 26 '20 at 15:39
  • @111111 It should be fine then. I would just mention that you will need to initialize them through a reference and not a copy of the variant (Do not do `X x = std::get(y); x.data_[0] = 42;` but do `std::get(y).data_[0] = 42;` instead) to avoid undefined behaviour. – Fareanor Feb 26 '20 at 15:45
  • @111111 You're welcome. I've edited my answer accordingly. – Fareanor Feb 26 '20 at 15:56