Aggregate Initialization Safety in C++
Asked Answered
C

4

8

Suppose I have the following struct:

struct sampleData
{
       int x;
       int y;
};

And when used, I want to initialize variables of sampleData type to a known state.

sampleData sample = { 1, 2 } 

Later, I decide that I need additional data stored in my sampleData struct, as follows:

struct sampleData
{
       int x;
       int y;
       int z;
};

It is my understanding that the two field initialization left over from my pre-z data structure is still a valid statement, and will be compiled., populating the missing fields with default values.

Is this understanding correct? I have been working recently in Ada, which also allows aggregate initialization, but which would flag a similar issue as a compilation error. Assuming that my assumptions about the C++ code above are correct, is there a language construct which would recognize missing initialization values as an error?

Coleoptile answered 8/6, 2011 at 20:0 Comment(0)
B
5

Initialising variables that way is only supported with Aggregate Classes.

If you add constructor(s) then then problem goes away, but you'll need to change the syntax a little and you lose the ability to store the struct in a union (among other things).

struct sampleData
{
    sampleData(int x, int y) : x(x), y(y) {}
    int x;
    int y;
};

sampleData sample( 1, 2 );

Adding z (and changing the constructor) will mark sample( 1, 2 ) as a compile error.

Briton answered 8/6, 2011 at 20:6 Comment(5)
+1. And adding an (x,y,z) constructor where Z has a default value can give you the best of both worlds. Even if your default for Z is zero it helps document what you're doing,Announce
No, initializing variables that way is only supported with aggregate classes. All aggregate classes are POD types, but not all POD types are aggregate classes, and thus not all POD types support aggregate initialization.Maroc
This was my answer too (so +1), but there is a drawback to doing this. Technically this makes his struct no longer a POD. However, in practice most compilers seem to treat it as a POD, as long as nothing much more than this is done with it. I call structs like this "pseudoPODs"Coadjutress
@Coadjutress : In C++0x such a thing has a formal name -- 'standard-layout class'.Maroc
@Maroc - I had heard that. I still think "pseudoPOD" is a much better name. :-)Coadjutress
A
4

Yes, any elements you leave off of the initialization list will be initialized to zero (for POD scalar types) or using their default constructor (for classes).

The relevant language from the C standard is quoted here:

[6.7.8.21] If there are fewer initializers in a brace-enclosed list than there are elements or members of an aggregate, or fewer characters in a string literal used to initialize an array of known size than there are elements in the array, the remainder of the aggregate shall be initialized implicitly the same as objects that have static storage duration.

I am sure someone more motivated than I could find the corresponding language in one of the C++ specs...

Note that this implies that POD scalar elements are initialized as if you wrote "= 0". Which means it will correctly initialize pointers to NULL and floats to 0.0 even if their representations do not happen to be all-zero bytes. It also implies that it works recursively; if your struct contains a struct, the inner struct will be properly initialized as well.

Akanke answered 8/6, 2011 at 20:5 Comment(2)
For the purposes of working with structs, I suspect the C standard is fairly closed to the C++ specColeoptile
@Coleoptile : No, actually -- in C++, objects that have static storage duration are zero-initialized if they're POD types but are otherwise initialized according to the way they're defined; on the other hand, data members omitted from an aggregate initializer are always value-initialized.Maroc
M
4

As a followup to Nemo's answer with the C standardese, here is what the C++03 standard says:

§8.5.1/7:

If there are fewer initializers in the list than there are members in the aggregate, then each member not explicitly initialized shall be value-initialized.

§8.5/5:

To value-initialize an object of type T means:

  • if T is a class type with a user-declared constructor, then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
  • if T is a non-union class type without a user-declared constructor, then every non-static data member and base-class component of T is value-initialized;
  • if T is an array type, then each element is value-initialized;
  • otherwise, the object is zero-initialized

To zero-initialize an object of type T means:

  • if T is a scalar type, the object is set to the value of 0 (zero) converted to T;
  • if T is a non-union class type, each nonstatic data member and each base-class subobject is zero-initialized;
  • if T is a union type, the object’s first named data member) is zero-initialized;
  • if T is an array type, each element is zero-initialized;
  • if T is a reference type, no initialization is performed.
Maroc answered 8/6, 2011 at 20:15 Comment(1)
@Akanke : I just copied and pasted from a different answer of mine that was already formatted. No effort involved :-PMaroc
M
-2

Why not use

sampleData sample = { x: 1, y:2 } ;

?

But you'd still run into the problem of z being initialized to an unpredictable value, so it's better to define a constructor which sets all variables to well defined values.

Mika answered 8/6, 2011 at 20:8 Comment(4)
Because the question is tagged c++ and that's not C++?Maroc
so is sampleData sample = { 1, 2 } C++ then ?Mika
Indeed it is -- it's called 'aggregate initialization'.Maroc
This answer is now getting closer to being right as of C++20.Blakemore

© 2022 - 2024 — McMap. All rights reserved.