Почему анонимные союзы не могут содержать членов с нетривиальными конструкторами / деструкторами? - PullRequest
5 голосов
/ 09 декабря 2010

Я могу ошибаться, но основное объяснение, которое я нашел, состояло в том, что объединение не может инициализироваться, потому что оно не знает, какой конструктор члена вызывать.Компилятор не может автоматически сгенерировать конструктор для объединения.

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

Кроме того, почему член объединения не может иметь каких-либо пользовательских конструкторов?Предыдущее объяснение не относится к пользовательским конструкторам.

Обновление 1:

Пример:

struct SQuaternion
{
    union
    {
        S3DVector Axis;
        struct
        {
            float X;
            float Y;
            float Z;
        };
    };
    float W;
};

Примечание. Проблема здесь заключается в том, что объединение является анонимным,Как так, как можно назвать конструктор союза?Это кажется невозможным, просто потому, что у него нет имени, и ни по какой другой причине.Это было бы ужасной причиной, если бы это была простая лексическая проблема ...

Обновление 2: просто, оборачивая нарушающий член в ограждающую анонимную структуру, ошибка исчезает.Я полагаю, это самое близкое, что можно сделать с анонимным союзом.Факт, что это перестает быть проблемой, все еще кажется странным ...

Ответы [ 5 ]

13 голосов
/ 09 декабря 2010

Более серьезная причина будет: как профсоюз узнает, какой деструктор вызвать. Сам язык не отслеживает, какой член активен в объединении.

Кажется, что C ++ 0x разрешит нетривиальные типы в объединениях, и в этом случае вы будете вынуждены реализовать свой собственный конструктор (ы) и деструктор. (Последнее немного неясно из предложения , кажется, что деструктор объединения не будет вызывать деструкторы-члены, и деструктор для правильного должен вызываться вручную.)

2 голосов
/ 09 декабря 2010

Вероятно, можно было бы определить такую ​​вещь, но это открыло бы такую ​​банку с червями, что это почти наверняка не стоило бы:

  1. Какой объект построить?
  2. Если вы определяете конструктор объединения, как он создает один объект против другого?
  3. Вам нужно определить один конструктор для каждого элемента, не являющегося модулем?
  4. Каков синтаксис для назначения другого объекта тому, который назначен в настоящее время?
  5. Вы разрешаете интерпретировать один объект в контексте другого? Этот в настоящее время разрешен для профсоюзов и является очень распространенным вариантом использования (union { char c[4]; int n; } u; u.n=1234; cout << u.c[1];).
  6. Как компилятор узнает, какой деструктор вызвать?
  7. Как компилятор узнает, какой конструктор копирования вызывать при копировании объединения?

(Я что-нибудь пропустил?)

Я подозреваю, что он просто вошел в слишком жесткую корзину.

1 голос
/ 09 декабря 2010

Я думаю, что вы правы, профсоюзы в C ++ недооценены.Это в значительной степени прямая копия союзов из C, что означает, что они не служат вариантами типов для C ++ .

. Для union в C ++ нет простого способапредставляют правильный тип варианта.Рассмотрим следующий код, если он был допустимым:

union X {
    int i;
    std::string s;
};

X x;
x.s = "Hello";
x.i = 23;

Никакое количество конструкторов или деструкторов для X не гарантирует, что присваивание в последней строке вызовет ~string перед сохранением 23.Чтобы компилятор мог это сделать, объединение должно содержать какой-то индикатор того, какой тип хранится.Вот почему все должно быть POD.Я не знаю причин различий между именованными и безымянными союзами, хотя это относится и к обоим.

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

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

Используйте массив символов для хранения, размещения нового для конструкции / назначения и прямого вызова правильного деструктора в вашем деструкторе.

Остерегайтесь проблемы с выравниванием - вам необходимо убедиться, что ваше сырое хранилище правильно выровнено для любого из типов, которые вы в него помещаете.Одним из способов сделать это является его динамическое распределение.Другой способ - поместить массив char в объединение с любым встроенным типом, который имеет наибольшее требование выравнивания (если вы не знаете: все).

Единственное, что отличается в использовании, отОбъединение, которое вы хотите, состоит в том, что вместо открытых элементов данных int a, float b, string c вам придется предоставлять средства доступа, которые возвращают прокси-объект (возможно, ссылку на сам объект), который способен назначатьправильно, что означает вызов деструктора для старого типа первым.Затем вы можете написать x.i() = 23 вместо x.i = 23.

Или вы можете использовать Boost.Variant .

0 голосов
/ 09 декабря 2010

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

Объединения, имеющие членов: от 9,5

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

0 голосов
/ 09 декабря 2010

Мне кажется, этот код работает нормально:

typedef union uAA {
    double dVal;
    int iVal[2];

    uAA() : dVal(3.22) {}
} UAA;

main() {
    UAA rdata;

    printf("Array output: %d %d \nDouble output: %lf \n",
        rdata.iVal[0], rdata.iVal[1], rdata.dVal);
}
...