Fun with designated initializers
C++20 (via P0329)
introduced a relatively nerfed form of designated-initializer (.field = 42) syntax.
Unlike C, C++20 doesn’t support out-of-order initializers, any mixture of designated
and non-designated initializers, or array-element designators.
Array-element designators like [0] = 42 aren’t supported by standard C++. Why not?
Read the spoiler in [diff.dcl],
or, consider Clang’s behavior on the following program
(Godbolt).
bool oldstyle() {
#define ZERO 0
std::any a[3] = {
[ZERO] {},
};
return a[0].has_value(); // false
}
bool modernized() {
constexpr int zero = 0;
std::any b[3] = {
[zero] {},
};
return b[0].has_value(); // true
}
Spoiler: Clang treats [0]{} as a C99-style designated initializer for an array element,
using a kind of uniform-initialization style: “Initialize element 0 from this bag of
constructor arguments (which happens to be empty.”
But Clang rightly treats [zero]{} as a lambda-expression capturing the local variable
zero, omitting the parameter list (which is legal, though I wish it weren’t!),
and having an empty body. (That’s legal C++: all compilers must parse it that way!)
Ironically, GCC — which originally introduced the designated-initializer extension —
has never supported Clang’s =-less syntax in C++ mode; although GCC in C mode only
does support a =-less syntax described in the manual as “obsolete since GCC 2.5.”
// in GNU C only
int i[3] = { [1] 42 };
struct A {};
struct A a[3] = { [1] {} };
Clang supports that syntax in both C and C++ modes. In C++ mode, the initialization
of i gives -Wgnu-designator, but (since Clang 10) the initialization of a doesn’t.
(Godbolt.)
warning: use of GNU 'missing =' extension in designator [-Wgnu-designator]
3 | int i[3] = { [1] 42 };
warning: array designators are a C99 extension [-Wc99-designator]
4 | struct A a[3] = { [1] {} };
Footnote: As far as I know, even in C, where out-of-order initializers are legal, no compiler permits array-element initializers whose indices are determined at runtime. You might wish
int a[5] = { [argc] = 1 };
were equivalent to
int a[5] = {};
a[argc] = 1;
but in fact both GCC and Clang reject it.
