С++ 11 пустой список Инициализация объединения - гарантируется ли инициализация полной длины объединения?

В С++ 11 у меня есть следующий союз:

union SomeData
{
    std::uint8_t Byte;
    std::uint16_t Word;
    std::uint32_t DWord;
    unsigned char String[128];
};

Если я инициализирую объединение таким образом,

SomeData data {};

Гарантировано ли, что все содержимое союза будет "нулевым"? Перефразируй; является пустым списком-инициализатором объединения, функционально эквивалентным memset-union union to Zero?:

memset(&data, 0, sizeof(data));

В частности, меня беспокоят строковые данные. Я хотел бы обеспечить, чтобы вся длина строки содержала нули. Кажется, он работает в моем текущем компиляторе, но действительно ли язык спецификации гарантирует это всегда?

Если нет: существует ли лучший способ инициализировать полную длину объединения до нуля?

Ответ 1

Нет, не гарантируется, что весь союз будет обнулен. Только первый объявленный член союза, плюс любое дополнение, гарантированно обнуляется (доказательство ниже).

Итак, чтобы вся область памяти объекта union была обнулена, у вас есть следующие параметры:

  • Закажите элементы таким образом, чтобы наибольший член был первым и, следовательно, был обнулен.
  • Используйте std::memset или эквивалентную функциональность. Чтобы предотвратить случайное забывание об этом, вы можете, конечно, дать SomeData конструктор по умолчанию, который будет называть это.

Цитата С++ 11:

8.5.4 [dcl.init.list]/3

Список-инициализация объекта или ссылки типа T определяется следующим образом:

  • Если в списке инициализаторов нет элементов, а T - тип класса с конструктором по умолчанию, объект значение инициализации.

8.5 [dcl.init]/7

Для инициализации значения объекта типа T означает:

  • Если T является (возможно, cv-квалифицированным) типом класса (раздел 9) с предоставленным пользователем конструктором (12.1), то вызывается конструктор по умолчанию для T (и инициализация плохо сформирована, если T не имеет доступного значения по умолчанию Конструктор);
  • если T является (возможно, cv-квалифицированным) классом типа non-union без конструктора, предоставленного пользователем, тогда объект инициализируется нулем и, если T s неявно объявленный конструктор по умолчанию является нетривиальным, этот конструктор называется.
  • ...
  • в противном случае объект инициализируется нулем.

8.5 [dcl.init]/5:

Для нулевой инициализации объекта или ссылки типа T означает:

...

  • Если T является (возможно, cv-квалифицированным) типом объединения, объекты первого нестатического именованного элемента данных инициализируются нулем и заполнение инициализируется нулевыми битами;

Из этих кавычек вы можете видеть, что использование {} для инициализации data приведет к инициализации объекта инициализацией объекта (поскольку SomeData - это тип класса с конструктором по умолчанию).

Значение-инициализация объединения без предоставленного пользователем конструктора по умолчанию (который SomeData) означает нуль-инициализацию.

Наконец, нуль-инициализация объединения означает нуль-инициализацию первого нестатического имени элемента данных.

Ответ 2

Весь союз будет обнулен. Точнее, первый член объединения будет инициализирован по умолчанию и все остальные байты в объединении будут установлены в 0 как дополнение.

Ссылки (подчеркните мои):

8.5 Инициализаторы [dcl.init]
...

5 Для нулевой инициализации объекта или ссылки типа T означает:
...
- если T является (возможно, cv-квалифицированным) типом объединения, объекты первого нестатического именованного элемента данных ноль инициализируются , а заполнение инициализируется нулевыми битами;

Это означает, что первый член объединения (здесь std::uint8_t Byte;) будет инициализирован равным 0 и что все остальные байты в объединении будут установлены в 0, поскольку они являются байтами заполнения.


Но будьте осторожны. Как заявлено Angew "padding" чудесно underspecified в стандарте, и компилятор C мог интерпретировать, что байты заполнения в объединении - это только байты, которые следуют за наибольшим членом. Я бы действительно счел это странным, потому что изменения совместимости были специально задокументированы, а предыдущие версии (C) сначала инициализировали все до 0, а затем сделали определенную инициализацию. Но новый разработчик не мог знать об этом...

TL/DR: Я действительно считаю, что цель стандарта заключается в том, что все байты в объединении установлены в 0 в примере OP, но для критически важной программы я обязательно добавлю явный конструктор 0...