Date: Mon, 16 Jan 2023 18:49:54 +0100
Hi!
I was reading the spec and found that it appears to apply a "wording trick"
here, to make a function viable but render the call illformed (as we know
from several other cases, for example with lvalue references to bitfield,
yet there the trick is more obvious):
"[*Note 1 <https://eel.is/c++draft/over.ics.list#note-1>*:
Aggregate initialization does not require that the members are declared in
designation order. <https://eel.is/c++draft/over.ics.list#2.sentence-2>
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] <https://eel.is/c++draft/dcl.init.list>).
<https://eel.is/c++draft/over.ics.list#2.sentence-3>"
(this follows from the normative wording for the simple non-nesting case..
I take the note because it clears the air here and shows the intention).
However, as [dcl.init.aggr] notes (and again it follows from the normative
wording anyway), if a member is initialized by {...} again, then the rules
of list initialization applies. Therefore I think that [dcl.init.aggr]
applies constraints on the order of members indirectly:
"[*Note 3 <https://eel.is/c++draft/dcl.init.aggr#note-3>*:
If an initializer is itself an initializer list, the element is
list-initialized, which will result in a recursive application of the rules
in this subclause if the element is an aggregate.
<https://eel.is/c++draft/dcl.init.aggr#4.2.sentence-4>
— *end note*]"
I take all this to mean that the nested case is to be treated differently
than the non-nested case, if going by the letter of the spec, but the
implementations I tried (gcc and clang) treat the followinig example as
being ambiguous, rather than selecting "C<B>":
struct A {
int x; int y;
};
struct B {
int y; int x;
};
template<typename T>
struct C {
T a;
};
void f(C<A> a);
void f(C<B> b);
int main() {
f({{ .y = 1, .x = 2}});
}
I was reading the spec and found that it appears to apply a "wording trick"
here, to make a function viable but render the call illformed (as we know
from several other cases, for example with lvalue references to bitfield,
yet there the trick is more obvious):
"[*Note 1 <https://eel.is/c++draft/over.ics.list#note-1>*:
Aggregate initialization does not require that the members are declared in
designation order. <https://eel.is/c++draft/over.ics.list#2.sentence-2>
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] <https://eel.is/c++draft/dcl.init.list>).
<https://eel.is/c++draft/over.ics.list#2.sentence-3>"
(this follows from the normative wording for the simple non-nesting case..
I take the note because it clears the air here and shows the intention).
However, as [dcl.init.aggr] notes (and again it follows from the normative
wording anyway), if a member is initialized by {...} again, then the rules
of list initialization applies. Therefore I think that [dcl.init.aggr]
applies constraints on the order of members indirectly:
"[*Note 3 <https://eel.is/c++draft/dcl.init.aggr#note-3>*:
If an initializer is itself an initializer list, the element is
list-initialized, which will result in a recursive application of the rules
in this subclause if the element is an aggregate.
<https://eel.is/c++draft/dcl.init.aggr#4.2.sentence-4>
— *end note*]"
I take all this to mean that the nested case is to be treated differently
than the non-nested case, if going by the letter of the spec, but the
implementations I tried (gcc and clang) treat the followinig example as
being ambiguous, rather than selecting "C<B>":
struct A {
int x; int y;
};
struct B {
int y; int x;
};
template<typename T>
struct C {
T a;
};
void f(C<A> a);
void f(C<B> b);
int main() {
f({{ .y = 1, .x = 2}});
}
Received on 2023-01-16 17:50:08