Инициализация объединения с нетривиальным конструктором

У меня есть структура, в которой я создаю пользовательский конструктор для инициализации членов до 0. Я видел в старых компиляторах, что в режиме освобождения, не делая memset до 0, значения не инициализируются.

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

Итак, вопрос 1. Предоставляет ли встроенный конструктор конструктора по умолчанию гарантировать, что все члены структуры будут инициализированы нулями? Нетривиальный конструктор просто выполняет набор всех членов в "0", чтобы обеспечить чистую структуру.

Вопрос 2: Если конструктор должен быть указан в базовой структуре, как можно реализовать объединение, чтобы содержать этот элемент и обеспечить нулевой базовый элемент 0?

Ответ 1

Вопрос 1: Конструкторы по умолчанию инициализируют элементы POD до 0 согласно стандарту С++. См. Цитируемый текст ниже.

Вопрос 2: Если конструктор должен быть указан в базовом классе, то этот класс не может быть частью объединения.

Наконец, вы можете предоставить конструктор для вашего объединения:

union U 
{
   A a;
   B b;

   U() { memset( this, 0, sizeof( U ) ); }
};

Для Q1:

Из С++ 03, 12.1 Конструкторы, pg 190

Неявно определенный конструктор по умолчанию выполняет набор инициализаций класс, который будет выполняться с помощью написанного пользователем конструктора по умолчанию для этого класса с пустым списком mem-initializer (12.6.2) и пустым телом функции.

Из С++ 03, 8.5 Инициализаторы, pg 145

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

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

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

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

Для Q2:

Из С++ 03, 12.1 Конструкторы, pg 190

Конструктор тривиален, если он неявно объявлен по умолчанию и если:

  • его класс не имеет виртуальных функций (10.3) и нет виртуальных базовых классов (10.1) и
  • все прямые базовые классы его класса имеют тривиальные конструкторы, а
  • для всех нестатических членов данных своего класса, которые относятся к типу класса (или массиву из них), каждый такой класс имеет тривиальный конструктор

Из С++ 03, 9.5 Союзы, pg 162

Объединение может иметь функции-члены (включая конструкторы и деструкторы), но не виртуальные (10.3) функции. Союз не должен иметь базовые классы. Объединение не должно использоваться как базовый класс. Объектом класса с нетривиальным конструктором (12.1) является нетривиальный конструктор копии (12.8), нетривиальный деструктор (12.4) или нетривиальный оператор присваивания копии (13.5.3, 12.8) не может быть членом объединения, а также массив таких объектов

Ответ 2

Все изменилось к лучшему на С++ 11.

Теперь вы можете законно сделать это, как описанный Stroustrup (я дошел до этой ссылки из статьи Википедии о С++ 11).

Пример в Википедии выглядит следующим образом:

#include <new> // Required for placement 'new'.

struct Point {
    Point() {}
    Point(int x, int y): x_(x), y_(y) {}
    int x_, y_;
};

union U {
    int z;
    double w;
    Point p; // Illegal in C++03; legal in C++11.
    U() {new(&p) Point();} // Due to the Point member, a constructor
                           // definition is now *required*.
};

Страуструпу идет немного подробнее.

Ответ 3

Члены союза AFAIK могут не иметь конструкторов или деструкторов.

Вопрос 1: нет, нет такой гарантии. Любой член POD, не содержащийся в списке инициализации конструктора, инициализируется по умолчанию, но с указанным конструктором и имеет список инициализаторов. Если вы не определяете конструктор или вы определяете конструктор без списка инициализаторов и пустого тела, члены POD не будут инициализированы.

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

Вопрос 2: вы всегда можете инициализировать структуры/объединения следующим образом:

struct foo
{
    int a;
    int b;
};

union bar
{
    int a;
    foo f;
};

bar b = { 0 };

Ответ 4

Как упоминалось в комментарии Грега Роджерса к сообщению unesen, вы можете дать вашему объединению конструктор (и деструктор, если хотите):

struct foo
{
    int a;
    int b;
};

union bar
{
    bar() { memset(this, 0, sizeof(*this)); }

    int a;
    foo f;
};

Ответ 5

Можете ли вы сделать что-то подобное?

class Outer
{
public:
    Outer()
    {
        memset(&inner_, 0, sizeof(inner_));
    }
private:
    union Inner
    {
        int qty_;
        double price_;
    } inner_;
};

... или что-то вроде этого?

union MyUnion
{
    int qty_;
    double price_;
};

void someFunction()
{
    MyUnion u = {0};
}

Ответ 6

Вам придется подождать, когда С++ 0x будет поддерживаться компиляторами, чтобы получить это. До тех пор, извините.