C++ Logo


Advanced search

Designated initializers in overload resolution for nested members

From: Johannes Schaub <schaub.johannes_at_[hidden]>
Date: Mon, 16 Jan 2023 18:49:54 +0100
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>).

(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.
— *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