Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this code using designated initializers in function parameters goes from ambiguous to not compiling when removing one function?

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

like image 610
NoSenseEtAl Avatar asked May 28 '21 19:05

NoSenseEtAl


People also ask

What is designated initialization in C?

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.

Does order in designated initializer have to match order in struct?

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:

Can we combine initializers for arrays and structures?

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.

What is the Order of an initializer in C?

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,...


1 Answers

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.

like image 67
Barry Avatar answered Oct 18 '22 21:10

Barry