Enum bitfield и агрегатная инициализация

Следующий код принят clang 6.0.0, но отклоняется gcc 8.2

enum class E {
  Good, Bad,
};

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

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

Пример живой болтовки

GCC жалуется

error: не удалось преобразовать ' {Good, 100} ' из ' <brace-enclosed initializer list> ' в ' S '

Который правильный? Где в стандарте говорят об этой ситуации?

Ответ 1

Это должно быть хорошо сформировано, и поэтому это ошибка gcc.

Мы заканчиваем агрегатную инициализацию через [stmt.return] p2, который гласит:

... Оператор return с бин-init-списком инициализирует объект или ссылку, которая должна быть возвращена из функции командой copy-list-initialization ([dcl.init.list]) из указанного списка инициализаторов....

затем [dcl.init.list] p3.2 говорит:

В противном случае, если T является агрегатом, выполняется агрегатная инициализация ([dcl.init.aggr])....

На этом этапе мы можем задаться вопросом, является ли это сужающим преобразованием и поэтому плохо сформировано, но [dcl.init.list] p7 не имеет каких-либо положений, которые охватывают этот случай, и никакие другие случаи в [dcl.init.list] не применяются, чтобы сделать это плохо сформировалось.

Мы можем видеть аналогичный пример, который удаляет перечисление из уравнения, но сохраняет бит-поле, но ни gcc, ни clang не дает нам сужущей диагностики преобразования, которая, как мы ожидаем, будет иметь место, хотя эта аналогичная проблема покрывается [dcl.init.list] p7.4, хотя и не плохо сформирован:

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

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

видеть вживую в godbolt

Поскольку наблюдаемый gcc, похоже, не имеет проблемы в других контекстах, т.е.

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

Таким образом, у вас есть возможности для работы.

Ответ 2

return {E::Good, 100}; выполняет инициализацию списка копий возвращаемого значения. Эффект от этой инициализации списка представляет собой агрегатную инициализацию.

Итак, S - совокупность? Описание агрегата зависит от того, какую версию C++ вы используете, но во всех случаях S должен быть агрегатом, поэтому это должно компилироваться. У Clang (и MSVC) есть правильное поведение.

Однако исправление прост. Измените оператор return, чтобы вернуть правильно напечатанный объект:

return S{E::Good, 100};