Битовое поле Enum и агрегатная инициализация

Следующий код принимает 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};
}

Живой пример Godbolt

GCC жалуется

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

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


person Kan Li    schedule 05.09.2018    source источник
comment
Интересно, что GCC выдает ложное предупреждение 10:9: warning: 'S::e' is too small to hold all values of 'enum class E' для любого размера битового поля, кроме 32, а clang не предупреждает об этом, даже когда диапазон enum действительно не подходит. возможно, эта особая обработка битовых полей отчасти является причиной того, что GCC не может выполнить агрегатную инициализацию?   -  person Gem Taylor    schedule 06.09.2018


Ответы (2)


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

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

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

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

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

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

Мы можем видеть на аналогичном примере, который удаляет enum из уравнения, но сохраняет битовое поле, показывает, что ни 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;
}

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

person Shafik Yaghmour    schedule 07.09.2018

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

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

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

return S{E::Good, 100};
person 1201ProgramAlarm    schedule 06.09.2018
comment
вместо Из описания инициализации списка, используемого в операторе возврата, было бы хорошо предоставить стандартные кавычки и, в частности, указать, почему здесь не следует применять агрегированную инициализацию - person M.M; 06.09.2018
comment
@ 1201ProgramAlarm Я думаю, вы правы, но я также думаю, что gcc пытается сузить разговор о перечислении с ограниченной областью (с int в качестве базового типа) к битовому полю, что запрещено в инициализации списка, которая не прямой, как и в случае с возвратом, я думаю, что это потому, что если вы превратите перечисление в unscoped (для которого разрешено неявное преобразование в int) или увеличите битовое поле до 32, чтобы произошло идеальное совпадение, ошибка исчезнет даже оставляя инициализацию как инициализацию списка копирования - person Jans; 06.09.2018
comment
@xhamr После дальнейшего расследования я изменил свой вердикт и переписал ответ. - person 1201ProgramAlarm; 06.09.2018