Aggregate / designated initialization of c++ struct: Refer directly to another field
Asked Answered
S

1

1

When using aggregate / designated initialization of a struct it is possible to refer to another field like this:

#include <stdio.h>


int main()
{
  struct
  {
    int a;
    int b;
  }
  s = 
  {
    .a = 3,
    .b = s.a + 1,
  };

  return 0;
}

We use s.a in the initialization of s.b. However, we need to refer to s.a through s. Is it possible to refer directly to s.a as just .a, or similar? This would for instance make it possible to use the same syntax when initializing arrays of structs like here:

int main()
{
  struct
  {
    int a;
    int b;
  }
  s[] = 
  {
    {
      .a = 3,
      .b = s[0].a + 1,
    },
    {
      .a = 5,
      .b = s[1].a - 1,
    }
  };

  return 0;
}

Here we could maybe write something like .b = .a - 1 instead of .b = s[1].a - 1.

Swifter answered 12/11, 2020 at 19:1 Comment(5)
Even if you could refer directly to a member in the initializing expression, how would that help in the array case? Given .b = .a - 1 how would you distinguish between .b = s[0].a - 1 and .b = s[1].a - 1?Stoke
@Stoke They would be distinguished through the scope, like if .a was a normal variable.Swifter
Ok, let's say that worked, would .a refer to s[0].a or s[1].a? Both are in the same scope.Stoke
@Stoke If the brace initialiers for each struct were treated as a scope, then they would be in different scopes. Of course this is probably a theoretical discussion since it doesn't seem to work like that.Swifter
Indeed, it doesn't work like that, but there's nothing wrong with theory :) I see now what you want the code to do, I misunderstood that. And yes, that's a reasonable thing to expect the code to do, except for the rules not allowing it :pStoke
P
3

Is it possible to refer directly to s.a as just .a, or similar?

No, .a in this context is a designator, and it cannot be referred to in the brace-or-equal-initializer used to initialize a given data member by means of its matching designator.

struct Bar { int a; };

struct Foo {
    int a;
    Bar b;
};

int main() {
    Foo f = {
     //     ^ start of braced-init-list
        .a
     // ^^ designator (for data member of Foo)
            = 1,
     //     ^^^ initializer-clause
        .b{.a = 2}
     //   ^^^^^^^^ braced-init-list
     //   referring to a designator of a 
     //   data member of Bar
    };
}

Details

Designated initializers, a new C++20 feature introduced by P0329R4, are part of the grammar for braced-init-lists:

braced-init-list:
  { initializer-list ,opt }
  { designated-initializer-list ,opt }
  { }

where the grammar of designated-initializer-list is:

designated-initializer-list:
  designated-initializer-clause
  designated-initializer-list , designated-initializer-clause

and, where the grammar for the individual designated-initializer-clause:s is:

designated-initializer-clause:
  designator brace-or-equal-initializer

and where finally, a designator is:

designator:
  . identifier

Now, a brace-or-equal-initializer does not allow, in any way, to refer to another designator of the same object. Note here the difference here between designators and other identifiers, e.g. referring to data members of the struct as part of the initializer-clause or nested braced-init-list:s of the brace-or-equal-initializer.

Paulownia answered 12/11, 2020 at 19:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.