Путаница в том, что C явно разрешает выбор типов через объединение, тогда как C ++ (C ++ 11) не имеет такого разрешения.
c11
6.5.2.3 Члены структуры и объединения
95) Если член, используемый для чтения содержимого объекта объединения, не совпадает с членом, последним использовавшимся для хранения значения в объекте, соответствующая часть объектного представления значения переинтерпретируется как представление объекта в новом type, как описано в 6.2.6 (процесс, иногда называемый «type punning»). Это может быть ловушка.
Ситуация с C ++:
C ++ 11
9.5 Союзы [class.union]
В объединении максимум один из нестатических элементов данных может быть активным в любое время, то есть значение максимум одного из нестатических элементов данных может быть сохранено в объединении в любое время.
Позднее в C ++ появился язык, позволяющий использовать объединения, содержащие structs с общими начальными последовательностями; это, однако, не позволяет набирать текст.
Для того, чтобы определить , является ли объединение типа каламбурным это разрешено в 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 для неактивного члена объединения (поэтому присвоение неактивному члену без построения - это нормально), оно считается неинициализированным.