Путаница в том, что C явно разрешает выбор типов через объединение, тогда как C ++ (C ++ 11) не имеет такого разрешения.
c11
6.5.2.3 Члены структуры и объединения
95) Если член, используемый для чтения содержимого объекта объединения, не совпадает с членом, последним использовавшимся для хранения значения в объекте, соответствующая часть объектного представления значения переинтерпретируется как представление объекта в новом type, как описано в 6.2.6 (процесс, иногда называемый «type punning»). Это может быть ловушка.
Ситуация с C ++:
C ++ 11
9.5 Союзы [class.union]
В объединении максимум один из нестатических элементов данных может быть активным в любое время, то есть значение максимум одного из нестатических элементов данных может быть сохранено в объединении в любое время.
Позднее в C ++ появился язык, позволяющий использовать объединения, содержащие struct
s с общими начальными последовательностями; это, однако, не позволяет набирать текст.
Для того, чтобы определить , является ли объединение типа каламбурным это разрешено в C ++, мы должны искать дальше. Напомним, чтоc99 является нормативным справочником для C ++ 11 (и C99 имеет язык, аналогичный C11, разрешающий использование типов объединения):
3.9 Типы [basic.types]
4 - Объектное представление объекта типа T - это последовательность из N беззнаковых char объектов, занятых объектом типа T, где N равно sizeof (T). Представление значения объекта - это набор битов, содержащих значение типа T. Для тривиально копируемых типов представление значения - это набор битов в представлении объекта, который определяет значение, которое является одним дискретным элементом реализации. определенный набор значений. 42
42) Цель состоит в том, чтобы модель памяти C ++ была совместима с моделью памяти ISO / IEC 9899 языка программирования C.
Это становится особенно интересно, когда мы читаем
3.8 Время жизни объекта [basic.life]
Время жизни объекта типа T начинается, когда: - получено хранилище с надлежащим выравниванием и размером для типа T, и - если объект имеет нетривиальную инициализацию, его инициализация завершена.
Таким образом, для примитивного типа (который ipso facto имеет тривиальную инициализацию), содержащегося в объединении, время жизни объекта охватывает, по крайней мере, время жизни самого объединения. Это позволяет нам вызывать
3.9.2 Составные типы [basic.compound]
Если объект типа T расположен по адресу A, говорят, что указатель типа cv T *, значением которого является адрес A, указывает на этот объект, независимо от того, как было получено значение.
Предполагая, что интересующая нас операция - это определение типа, то есть получение значения неактивного члена объединения, и учитывая вышеизложенное, что у нас есть действительная ссылка на объект, на который ссылается этот член, эта операция будет lvalue-to -rvalue преобразование:
4.1 Преобразование Lvalue в rvalue [conv.lval]
Значение glvalue нефункционального типа, не являющегося массивом, T
может быть преобразовано в prvalue. Если T
это неполный тип, программа, которая требует этого преобразования, плохо сформирована. Если объект, на который ссылается glvalue, не является объектом типа T
и не является объектом производного типа T
, или если объект не инициализирован, программа, которая требует этого преобразования, имеет неопределенное поведение.
Тогда возникает вопрос, инициализируется ли объект, который является неактивным членом объединения, хранилищем для активного члена объединения. Насколько я могу судить, это не так, хотя, если:
- объединение копируется в
char
хранилище массива и обратно (3.9: 2), или
- объединение побайтно копируется в другое объединение того же типа (3.9: 3), или
- доступ к объединению через языковые границы осуществляется программным элементом, соответствующим ISO / IEC 9899 (насколько это определено) (3.9: 4, примечание 42), затем
доступ к объединению для неактивного члена определен и определен так, чтобы следовать представлению объекта и значения, доступ без одного из вышеуказанных промежуточных положений является неопределенным поведением. Это имеет значение для оптимизаций, которые могут быть выполнены в такой программе, поскольку реализация, конечно, может предполагать, что неопределенное поведение не возникает.
То есть, хотя мы можем законно сформировать lvalue для неактивного члена объединения (поэтому присвоение неактивному члену без построения - это нормально), оно считается неинициализированным.