Enum bitfield and aggregate initialization

Viewed 532

The following code is accepted by clang 6.0.0 but rejected by gcc 8.2

enum class E {
  Good, Bad,
};

struct S {
  E e : 2;
  int dummy;
};

S f() {
  return {E::Good, 100};
}

Live godbolt example

The GCC complains

error: could not convert '{Good, 100}' from '<brace-enclosed initializer list>' to 'S'

Which one is correct? Where in the standard talks about this situation?

2 Answers

return {E::Good, 100}; performs copy list initialization of the return value. The effect of this list initialization is an aggregate initialization.

So is S an aggregate? The description of an aggregate varies depending on which version of C++ you're using, but in all cases S should be an aggregate so this should compile. Clang (and MSVC) have the correct behavior.

The fix is simple, though. Change your return statement to return the correctly typed object:

return S{E::Good, 100};

This should be well-formed and so this is a gcc bug.

We end up at aggregate initialization via [stmt.return]p2 which says:

… A return statement with a braced-init-list initializes the object or reference to be returned from the function by copy-list-initialization ([dcl.init.list]) from the specified initializer list. …

then [dcl.init.list]p3.2 says:

Otherwise, if T is an aggregate, aggregate initialization is performed ([dcl.init.aggr]). …

At this point we may wonder if this is a narrowing conversion and therefore ill-formed but [dcl.init.list]p7 does not have any clauses that cover this case and no other cases in [dcl.init.list] apply to make this ill-formed.

We can see with a similar example which removes enum from the equation but keeps the bit-field shows neither gcc nor clang gives us a narrowing conversion diagnostic, which we expect to be the case, although this similar problem is covered by [dcl.init.list]p7.4 although not ill-formed:

struct S2 {
    int e : 2 ;
    int dummy ;
} ;

S2 foo( int x ) {
   return {x, 100} ;
}

see it live in godbolt

As observed gcc does not seem to have a problem in other contexts i.e.

S f(E e1, int x) {  
  S s {e1,100} ;
  return s;
}

So you do have work-arounds available.

Related