Ответ 1
(Этот ответ был написан для С++ 03, ситуация, возможно, изменилась с С++ 11)
Я не могу представить никаких веских оснований для его исключения... более того, нет особо веских оснований для его включения. Союзы просто не используются настолько, чтобы иметь такое значение, и существуют серьезные ограничения на тип членов профсоюза, ограничения, препятствующие практике ОО. Эти ограничения - и бремя ручного отслеживания конкретной переменной (ов) в союзе, имеющей правомерность - означают в значительной степени первое, что вы хотите сделать с объединением, инкапсулировать его в класс, а затем вы можете наследовать от этого. Предоставление объединению в цепочку наследования просто распространяется вокруг.
Итак, все, что может дать случайным пользователям С++ впечатление, что союзы могут быть смешаны с их OO-ориентированным кодом, вызовет больше проблем, чем пользы. Союзы в основном являются взломом для сохранения памяти, быстрой, но неприятной интерпретации данных и вариантов реализации, и, как правило, являются деталями реализации, а не интерфейсом.
Вы добавили немного к своему вопросу с вашими изменениями... мысли следуют.
Если объединение разрешено в качестве базового класса, его данные могут использоваться производным классом. Если производному классу интересно использовать только один член объединения, этот способ можно использовать для сохранения памяти. Я думаю, это ненадлежащее наследование. В этом случае лучше иметь союз внутри производного класса?
Итак, мы обсуждаем:
union U { int i; double d; };
struct D : U { };
Члены данных U будут либо должны быть - неявно или явно - общедоступными, защищенными или частными. Если они публичные или защищенные, то они не инкапсулированы, и мы вернулись в сценарий "поделиться болью", упомянутый выше. U обладает меньшей способностью инкапсулировать их, чем класс, содержащий объединение. Если они являются частными, то они могут так же легко быть членом данных объединения в классе.
Если union разрешен как производный класс, он может использовать службы базового класса. Например, если Union имеет несколько типов данных. Как известно, можно использовать только один тип данных. Для каждого типа данных существует базовый класс для предоставления услуг для этого конкретного типа. В этом случае множественное наследование может использоваться для получения услуг всех базовых классов для всех типов данных в Союзе. Это также я чувствую как ненадлежащее использование наследования. Но есть ли эквивалентная концепция для достижения контента в этой точке?
Хорошо, здесь все странно. Итак, у нас есть:
union U : public A, private B { };
Во-первых, позвольте мне немного более подробно остановиться на чем-то. Союзы не могут содержать сложные, инкапсулированные объекты - они являются антитезой инкапсуляции. Вы в значительной степени ограничены данными POD и не можете иметь конструкторы не по умолчанию и т.д. Примечание. Я говорю о членах данных объединения, а не о самом союзе. Таким образом, A и B будут - если эти правила союза-содержания останутся - будут очень ограниченными, а способность U получить не особенно полезна.
Это приводит к вопросу о том, почему профсоюзы не могут безопасно управлять более сложными объектами. Ну, как они могли это сделать? Очевидным ответом является добавление скрытого перечисления, чтобы сказать, какой из них действителен, и некоторые правила о том, какой тип должен быть сконструирован по умолчанию, вызывая деструктор сначала, когда назначено другое поле перечисления, и т.д. И т.д. И т.д. Может быть, они должны бросить если кто-то что-то делает для члена, который в настоящее время не сконструирован? Звучит нормально?
Ну, во-первых, накладные расходы перечисления могут не потребоваться, так как клиентский код может использовать один член, а затем другой в известном-appopriate порядке. Проверки и исключения на языке сами по себе являются подобными накладными расходами... они могут быть учтены до одной проверки перед несколькими использованиями, если оставить код клиента. В обоих случаях вы будете платить за некоторые издержки управления, которые нужны только некоторым приложениям - очень не-С++-подход.
Игнорируя это, профсоюзы в любом случае не так просты. Как и перечисления, их использование призвано быть гибким, и было бы сложно связаться с компилятором, чтобы он мог очищать, проверять и автоматизировать его. Вы могли бы подумать, что "эйнусы сложны?", Но каждое перечисление - концептуально обобщенное - фактически произвольное множество независимых битовых полей различной ширины. Это нетривиально, чтобы описать, какие из них должны быть взаимоисключающими или зависимыми и т.д. Компилятор вообще не покупает проблемное пространство. Точно так же союз может иметь одно или несколько одновременно легитимных представлений по тем же данным, в то время как другие являются недействительными, с тонкостями для загрузки. Например: объединение int64_t, double и char [4] всегда может быть прочитано как int64_t или char [4] после того, как установлено как double, но наоборот может прочитать недопустимый двойник и вызвать undefined, если вы не перечитываете значения, полученные из двойника в более раннее время, возможно, в библиотеке сериализации/десериализации. Компилятор не хочет покупать управление этим, что должно было бы сделать, чтобы гарантировать, что члены объектов союзов подчиняются promises, неявному в их собственной инкапсуляции.
Вы спрашиваете: "Лучше ли иметь союз внутри производного класса?"... нет - обычно не работает, поскольку большинство объектов нельзя вставлять в объединение (см. выше). Вы сталкиваетесь с этой проблемой, является ли объединение внутри производного класса, или - через новые функции языка, которые вы постулируете, - объединение фактически является производным классом.
Я понимаю ваше разочарование. На практике люди иногда хотят объединения произвольных нетривиальных объектов, поэтому они взломают его жестким и грязным способом, используя реинтерпрет-листинг или подобное, управляя выравниванием памяти некоторого пространства, достаточно большого для набора объектов, которые они будут поддерживать (или - проще, но медленнее - указатель на них). Вы найдете такие вещи в варианте boost и в любых библиотеках. Но вы не можете извлечь из них... знание о надлежащем использовании, степень проверок безопасности и т.д. Просто не выводимо или не выражено в С++. Компилятор не собирается делать это за вас.