Consider the following code:
struct A{
int x;
int y;
};
struct B{
int y;
int x;
};
void func (A){
}
void func (B){
}
int main()
{
func({.y=1,.x=1});
}
For some reason both clang and gcc consider this code ambiguous even though it is known that order in designated initializer must match the order in struct, although for some reason clang allows it and just issues a warning:
ISO C++ requires field designators to be specified in declaration order; field 'y' will be initialized after field 'x' [-Wreorder-init-list]
Now the fun begins:
If I comment out the func(B)
code goes from ambiguous to not compiling.
That is what I consider super weird. Is there some logic behind this?
If the cause of my confusion is not clear:
if we have both func(A)
and func(B)
in code both gcc and clang consider the func({.y=1,.x=1})
ambiguous, but if I remove func(B)
from source then it gives an error(or warning in case of clang). In other words we went from 2 options to 0 options by removing 1 option.
godbolt
Designated initialization is a feature that is also available in the C programming language. However, it is more relaxed than in C++. In C, it is possible to perform out-of-order designated initialization, to mix designated initializers and regular initializers, to nest designators, and to initialize arrays with designators.
For some reason both clang and gcc consider this code ambiguous even though it is known that order in designated initializer must match the order in struct, although for some reason clang allows it and just issues a warning:
We can also combine designated initializers for arrays and structures. Writing code in comment? Please use ide.geeksforgeeks.org , generate link and share the link here.
Designated Initializers in C. Standard C90 requires the elements of an initializer to appear in a fixed order, the same as the order of the elements in the array or structure being initialized. In ISO C99 you can give the elements in random order, specifying the array indices or structure field names they apply to,...
The rule is in [over.ics.list]/2:
If the initializer list is a designated-initializer-list, a conversion is only possible if the parameter has an aggregate type that can be initialized from the initializer list according to the rules for aggregate initialization ([dcl.init.aggr]), in which case the implicit conversion sequence is a user-defined conversion sequence whose second standard conversion sequence is an identity conversion.
[Note 1: Aggregate initialization does not require that the members are declared in designation order. If, after overload resolution, the order does not match for the selected overload, the initialization of the parameter will be ill-formed ([dcl.init.list]).
[Example 1:
struct A { int x, y; }; struct B { int y, x; }; void f(A a, int); // #1 void f(B b, ...); // #2 void g(A a); // #3 void g(B b); // #4 void h() { f({.x = 1, .y = 2}, 0); // OK; calls #1 f({.y = 2, .x = 1}, 0); // error: selects #1, initialization of a fails // due to non-matching member order ([dcl.init.list]) g({.x = 1, .y = 2}); // error: ambiguous between #3 and #4 }
— end example]
— end note]
Basically, the rule that the designators have to name members is in [dcl.init.aggr] (so it counts for whether you can form a conversion sequence) but the rule that designators have to be in order is in [dcl.init.list] (so it's just like a later requirement that happens, and does not affect whether a conversion sequence can be formed - so it's not "sfinae-friendly").
In other words, this is fine:
struct A { int a; };
struct B { int b; };
void f(A);
void f(B);
void call_f() {
f({.a=1}); // ok, calls f(A)
}
Because you cannot initialize a B
from {.a=1}
according to the rules for aggregate initialization.
But your example (and the one quoted above) are ambiguous, since both satisfy the aggregate initialization rules but fail later.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With