C++ Equivalent to Designated Initializers

C++ Equivalent to Designated Initializers?

I'm not sure you can do it in C++. For the stuff that you need to initialize using designated initializers, you can put those separately in a .c file compiled as C99, e.g.:

// In common header file
typedef union my_union
{
int i;
float f;
} my_union;

extern const my_union g_var;

// In file compiled as C99
const my_union g_var = { .f = 3.14159f };

// Now any file that #include's the header can access g_var, and it will be
// properly initialized at load time

Alternative to designated initializers in C++

In C++ you have to write

int widths[101] = { 1 };
widths[10] = 2;
widths[100] = 3;

Iterable Designated Initializer Alternative in C++

C++20 added designated initializers to C++. There are some restrictions compared to C, in a couple of different directions. In your case, the main difference of note is that C++ has tightened up a bit of the type system so you can no longer initialize a char * from a string literal.

In C, you can initialize a char * from a string literal for the sake of backward compatibility, but you still have to treat it as if it were a char const *--that is, if you try to write to the string literal you get undefined behavior (and on a modern machine, you'll typically get something on the order of a seg fault that will kill your program).

C++ now demands that you recognize that limitation by using char const * explicitly. If we change your code to suit:

static struct {char const* tag; char const* msg;} help_info[] = 
{[0]={"tag0","msg0"}, [1]={"tag1", "msg1"}};

...and compile it as C++, it's perfectly fine.

Do note there are other limitations that don't exist in C (but they don't affect this code). For example, C++ demands that the initializers be in order, so in C you also do this:

static struct {char const* tag; char const* msg;} help_info[] = 
{[1]={"tag1", "msg1"}, [0]={"tag0","msg0"}};

...so the initializer for [1] precedes the initializer for [0], but in C++ that's forbidden.

What is the C++ equivalent to C's designated initializers?

No, C++ does not support C99's designated initializers. If you want to set individual members by name, you'll need to do it via assignment, e.g.

MyStruct test_struct = MyStruct();
test_struct.name = "buffer";
test_struct.func1 = test_func1;

Designated initializers in C++20

According to the C++ 20 Standard (9.3.1 Aggregates. p. #3)

(3.1) — If the initializer list is a designated-initializer-list, the
aggregate shall be of class type, the identifier in each designator
shall name a direct non-static data member of the class
, and the
explicitly initialized elements of the aggregate are the elements that
are, or contain, those members.

So you may not use the designated initializer list to initialize data members of base classes.

Use instead the usual list initialization like

Employee e1{ "John", "Wick", 40, 50000 };

or

Employee e1{ { "John", "Wick", 40 }, 50000 };

or as @Jarod42 pointed in a comment you can write

Employee e1{ { .name{"John"}, .surname{"Wick"}, .age{40} }, 50000 };

In this case the direct base class is initialized by a designated initializer list while the class Employe in whole is initialised by a non-designated initializer list.

What is a designated initializer in C?

Designated initialisers come in two flavours:

1) It provides a quick way of initialising specific elements in an array:

int foo[10] = { [3] = 1, [5] = 2 };

will set all elements to foo to 0, other than index 3 which will be set to 1 and index 5 which will be set to 2.

2) It provides a way of explicitly initialising struct members. For example, for

struct Foo { int a, b; };

you can write

struct Foo foo { .a = 1, .b = 2 };

Note that in this case, members that are not explicitly initialised are initialised as if the instance had static duration.


Both are standard C, but note that C++ does not support either (as constructors can do the job in that language.)

C++20: Force usage of designated initializers to emulate named function arguments

The best way is just code review. Tell people to use designated initializers. Designated initializers are awesome, people like using them. This is really more of a social problem than a technical one.

If you really want to give a nudge, you could always stick in some truly awful data members first, like so:

class Args {
struct Key { explicit Key() = default; };
struct StopIt { StopIt(Key) { } };

public:
StopIt asdfjkhasdkljfhasdf = Key();

int a;
int b;
};

Args is still an aggregate, we just have this extra leading data member spelled with whatever came out when I just banged on my keyboard. It has a default member initializer - however that initializer is private to Args and only Args knows how to construct it.

So the user cannot correctly provide an initializer for that particular member, they have to rely on the default member initializer to initialize it correctly. And since it goes first, they have to rely on designated initialization in order to be able to initialize any of the other members.

But also... this is kind of a silly thing to write? Just tell people to use designated initialization.


Technically, they could write f({Args().asdfjkhasdkljfhasdf, 1, 2}) in this case, but this seems like we're going for absurdity or spite at this point.

Why C++20 doesn't support out-of-order designated initializer?

Yes, the rationale is covered in Annex C (informative)
Compatibility
specifically [diff.dcl]p10 (emphasis mine):

Affected subclause: [dcl.init.aggr] Change: In C++, designated
initialization support is restricted compared to the corresponding
functionality in C. In C++, designators for non-static data members
must be specified in declaration order, designators for array elements
and nested designators are not supported, and designated and
non-designated initializers cannot be mixed in the same initializer
list. Example:

struct A { int x, y; };
struct B { struct A a; };
struct A a = {.y = 1, .x = 2}; // valid C, invalid C++
int arr[3] = {[1] = 5}; // valid C, invalid C++
struct B b = {.a.x = 0}; // valid C, invalid C++
struct A c = {.x = 1, 2}; // valid C, invalid C++

Rationale: In C++, members are destroyed in reverse construction order and the elements of an initializer list are evaluated in lexical order, so field initializers must be specified in order.
Array designators conflict with lambda-expression syntax.
Nested designators are seldom used.

The first revision of the proposal also discusses this topic:

To meet these expectations for guaranteed copy elision, we require the designators to appear
as a subsequence of the data member declaration sequence, so that the evaluation order
matches the declaration order, and it is also textually left­to­right in designated initialization

You can obtain the last revision here.

Designated initializers and omitted elements

Try this link.

The idea is to be able to refer to members of a complex type like structure during initialization. E.g.

struct s {
int a, b;
};

int main() {
struct s = { .b = 42, .a = -42 };
return 0;
}

The flexibility is gained from being order independent when specifying values. Remember this was added to the C99 standard and may not be supported by compilers which do not support C99 fully (or support an earlier version of the standard).



Related Topics



Leave a reply



Submit