-3
extern "C" {
    struct Foo {
        int field1;
        int field2;
    };
}

Foo foo = {0};

https://godbolt.org/z/faxeos4r6

warning: missing field 'field2' initializer [-Wmissing-field-initializers]
  1. there's no constructor, it's an extern "C" struct.
  2. it doesn't complain about field1, which would seem to imply that there's some aggregate or direct-initialization going on.. but see #1
  3. both field1 and field2 are definitely initialized.

Why is it complaining about field2 (and not field1)?

Spongman
  • 9,665
  • 8
  • 39
  • 58
  • 1
    It's a warning, because you might have done this accidentally. If you don't want those warnings, turn them off. – Tim Roberts Aug 28 '23 at 06:14
  • 1
    thanks, I understand how to use compilers. not my question. – Spongman Aug 28 '23 at 06:14
  • 1
    What compiler? I can't repro on my g++, but my psychic powers suggest you can just change your initialization to `Foo foo = {};` and be ok. That is `{}` instead of `{0}` and be ok. Not sure if the `extern "C"` has anything to do with this. – selbie Aug 28 '23 at 06:22
  • 1
    @TimRoberts is right, the compiler assumes you might miss to initialize `field2` accidently. If you do `Foo foo = {};`, the compiler will not warn. If you do `Foo foo = {0};`, the compiler assumes you might miss to initialize other fields. – 273K Aug 28 '23 at 06:23
  • but the compiler _does_ initialize both fields (see #3). so, why the warning? – Spongman Aug 28 '23 at 06:31
  • ... and if it's so bothered by `field2` not being initialized, why doesn't it warn if I just say `Foo foo;` ? – Spongman Aug 28 '23 at 06:37
  • Because with one argument you only initialize partially, without arguments you do not initialize at all. So it warns for the partial initialization case. Both full and no initialization are doing fine. – Pepijn Kramer Aug 28 '23 at 06:41
  • 1
    The compiler writers seem to say, if you want zero initialization, you should write `foo = {}`, if you want anything else, write all values explicitly. And since people (like you) might disagree with this rule, it's in `-Wextra`, not `-Wall` – Homer512 Aug 28 '23 at 06:57
  • 1
    Does this answer your question: https://stackoverflow.com/a/61240590/2216148 – Rodney Aug 28 '23 at 06:57
  • 1
    `{}` gives you *default* initialization, which is not necessarily *zero* initialization (though it is in this case) – Rodney Aug 28 '23 at 06:59
  • 2
    A compiler can warn about whatever it wants. That is, about whatever its authors think that it deserves a warning. Which constructs deserve warnings is a question of personal preferences and cannot be decided purely logically. – n. m. could be an AI Aug 28 '23 at 07:25
  • There's no such thing as an `extern "C" struct`. `extern "C"` just tells the compiler that the C++ code that it applies to will be linked to C code, and should use the calling convention and naming conventions of the corresponding C compiler. It does **not** mean "compile this as C code". – Pete Becker Aug 28 '23 at 12:49
  • Given that you've compiled with command line options that include `-Wall -Wextra -Wpedantic` (which, between them, enable a significant number of warnings, whether obscure/pedantic or not) why are *you* surprised that your compiler is issuing a warning? Doing that is more likely to result in warnings about perfectly correct constructs - like (in this case) omitted initialisers even when the standard specifies what happens. Some developers *like* to be warned about such things so - if you ratchet up the warning levels - you can expect more warnings. – Peter Aug 28 '23 at 14:09

1 Answers1

3

They complain about that for the same reason they may complain about:

if (a = 0) { ... }

It's a common error people often make (to assign to a variable in a conditional statement, rather than comparing with ==), despite the fact it's perfectly legal to do so.


In your case, it's considered a common-enough problem for people to initialise fewer fields than actually exist, so it lets you know about it.

That's why it's a warning rather than an error. It's telling you you should examine your code and ensure it's doing what you expect. If it is, you can almost certainly silence the warning with, for example, a compiler flag to turn it off, or a more localised #pragma. Or better yet, use the empty initialiser list {} rather than {0}.

Regarding your specific bullet points:

  • there's no constructor, it's an extern "C" struct: yes, that's correct. If it had a constructor, the initialisation would follow a different path through the standard to what I've documented below.

  • it doesn't complain about field1, which would seem to imply that there's some aggregate or direct-initialization going on.. but see #1: again, correct, but it's not complaining that the fields aren't initialised, just that you explicitly initialised some but not others. That's the possible problem it's bringing to your intention.

  • both field1 and field2 are definitely initialized: see previous point.

Based on that (and the analysis below), I would suggest using Foo foo = {}. A simple Foo foo will not initialise any of the fields, which is probably not what you want. But Foo foo = {} will:

  • explicitly initialise no fields; then
  • implicitly value-initialise the remaining fields (i.e., all fields).

And it's likely to get rid of the warning since you're not explicitly initialising only a subset.


For the language lawyers amongst us, this is covered in C++20 [dcl.init], which deals with the declarations with initialisations, such as your case of this question.

Specifically, Foo foo = {0} initialises the first field explicitly and the others based on certain rules. The equivalent functionality is to treat it as:

Foo foo;
foo.field1 = 0; // explicit init
foo.field2 = 0; // implicit/value init

That's due to the following sections in the standard, first the explicit initialisation of field1 with zero:

[dcl.init.aggr] (3.2) If the initializer list is an initializer-list, the explicitly initialized elements of the aggregate are the first n elements of the aggregate, where n is the number of elements in the initializer list.

The the implicit initialisation of all other fields as if they were initialised to {}:

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

Finally, the sections that state what happens when you initialise with {}. (3.11) is the first match to your scenario, from a long sequence of checks:

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

[dcl.init] (8) To value-initialize an object of type T means:
[dcl.init] (8.1) SOME IRRELEVANT STUFF
[dcl.init] (8.2) SOME IRRELEVANT STUFF
[dcl.init] (8.3) SOME IRRELEVANT STUFF
[dcl.init] (8.4) Otherwise, the object is zero-initialized.

Hence, zero initialisation of your int field is happening here even without your explicit request.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • yeah, all of that is fine, except it _does_ initialize field2 to zero. if I just say `Foo foo;` there's no warning, and **no** fields are initialized. – Spongman Aug 28 '23 at 06:33
  • @Spongman: I've added a lot more detail drawn from the actual standard. Bottom line is that `Foo foo;` initialises nothing, `Foo foo = {0};` explicitly initialises `field1` and value-initialises `field2` (to zero). – paxdiablo Aug 28 '23 at 07:11
  • @Spongman warnings need not follow any logic. The reason compilers warn is that compilers warn. The reason they do not warn for a different case is because they dont warn for the different case. The quotes from the standard are good background information, but one can only speculate why the compiler implementers chose to implement one but not the other warning. – 463035818_is_not_an_ai Aug 28 '23 at 07:12