Ответ 1
Тони Парк дал ответ, который очень близок к истине. Комитет С++ в принципе не думал, что стоит усилий, чтобы сделать профсоюзы сильной частью С++, подобно обработке массивов как наследие, которое нам нужно было наследовать от C, но не очень хотелось.
У профсоюзов есть проблемы: если мы разрешаем типы non-POD в объединениях, как они создаются? Это, безусловно, можно сделать, но не обязательно безопасно, и для любого рассмотрения потребуются ресурсы комитета. И конечный результат был бы менее чем удовлетворительным, потому что то, что действительно требуется на разумном языке, - это дискриминационные союзы, а простые профсоюзы C никогда не могут быть подняты до дискриминационных союзов, совместимых с C (что я могу себе представить, так или иначе).
Разрабатывать технические проблемы: поскольку вы можете обернуть объединение POD только в структуру, не теряя ничего, нет преимущества, позволяющего объединениям в качестве баз. С компонентами соединения только POD нет проблем с явными конструкторами, просто назначающими один из компонентов или с использованием битблица (memcpy) для созданного компилятором конструктора (или назначения) копии.
Такие союзы, однако, недостаточно полезны, чтобы беспокоиться, кроме как сохранить их, поэтому существующий C-код можно считать допустимым С++. Эти POD-единственные соединения разбиты на С++, потому что они не могут сохранить жизненно важный инвариант, который они имеют в C: любой тип данных может использоваться как тип компонента.
Чтобы сделать союзы полезными, мы должны разрешать конструктивные типы как члены. Это важно, потому что неприемлемо просто назначать компонент в теле конструктора, как самого объединения, так и любой закрывающей структуры: вы не можете, например, назначить строку неинициализированному компоненту строки.
Из этого следует, что необходимо инициализировать компонент объединения с mem-initialisers, например:
union X { string a; string b; X(string q) : a(q) {} };
Но теперь вопрос: что такое правило? Обычно правило состоит в том, что вы должны инициализировать каждый член и базу класса, если вы не делаете этого явно, конструктор по умолчанию используется для остатка, и если один тип, который явно не инициализирован, не имеет конструктора по умолчанию, он Ошибка [Исключение: конструкторы копирования, по умолчанию это конструктор экземпляра-члена].
Очевидно, что это правило не может работать для объединений: вместо этого правило должно быть: если в объединении есть хотя бы один элемент, не являющийся POD, вы должны явно инициализировать ровно один член в конструкторе. В этом случае не будет создан конструктор по умолчанию, конструктор копирования, оператор присваивания или деструктор, и если какой-либо из этих элементов действительно используется, они должны быть явно предоставлены.
Итак, теперь возникает вопрос: как бы вы написали, скажем, конструктор копирования? Разумеется, вполне возможно сделать и получить право, если вы создадите свой союз таким образом, скажем, созданы союзы событий X-Windows: с дискриминационным тегом в каждом компоненте, но вам придется использовать оператор размещения new для его выполнения, и вам придется сломать правило, которое я написал выше, которое на первый взгляд появилось правильно!
Как насчет конструктора по умолчанию? Если у вас нет одного из них, вы не можете объявить неинициализированную переменную.
Существуют и другие случаи, когда вы можете определить компонент извне и использовать новое место размещения для управления соединением извне, но это не конструктор копирования. Дело в том, что если у вас есть N компонентов, вам нужны N конструкторов, а у С++ есть некорректная идея о том, что конструкторы используют имя класса, что оставляет вас довольно короткими именами и заставляет вас использовать типы phantom, чтобы позволить перегрузке выбирать правый конструктор.. и вы не можете сделать это для конструктора копирования, так как его подпись исправлена.
Хорошо, так есть альтернативы? Наверное, да, но их не так легко придумать, и труднее убедить более 100 человек, о которых стоит подумать на трехдневном собрании, переполненном другими проблемами.
Жаль, что комитет не выполнил вышеприведенное правило: профсоюзы являются обязательными для выравнивания произвольных данных, а внешнее управление компонентами на самом деле не так сложно сделать вручную, а тривиально и полностью безопасно, когда код генерируется подходящий алгоритм, другими словами, правило обязательноеесли вы хотите использовать С++ в качестве целевого языка компилятора и по-прежнему создавать читаемый переносимый код. Такие объединения с конструктивными членами имеют много применений, но наиболее важным является представление фрейма стека функции, содержащей вложенные блоки: каждый блок имеет локальные данные в структуре, а каждая структура является объединенным компонентом, нет необходимости в каких-либо конструкторах или такой, компилятор будет просто использовать новое место размещения. Союз обеспечивает выравнивание и размер, а также свободный доступ к компонентам. [И нет другого подходящего способа получить правильное выравнивание!]
Поэтому ответ на ваш вопрос: вы задаете неправильный вопрос. Нет никакого преимущества для объединений POD, являющихся основами, и они, конечно же, не могут быть производными классами, потому что тогда они не были бы POD. Чтобы сделать их полезными, требуется некоторое время, чтобы понять, почему следует следовать принципу, используемому везде в С++: отсутствующие биты не являются ошибкой, если вы не попытаетесь их использовать.