La confusión es que C permite explícitamente la escritura de tipos mediante una unión, mientras que C ++ (c ++ 11) no tiene dicho permiso.
c11
6.5.2.3 Estructura y miembros del sindicato
95) Si el miembro utilizado para leer el contenido de un objeto de unión no es el mismo que el miembro utilizado por última vez para almacenar un valor en el objeto, la parte apropiada de la representación del objeto del valor se reinterpreta como una representación de objeto en el nuevo escriba como se describe en 6.2.6 (un proceso a veces llamado "punzonado de tipo"). Esto podría ser una representación trampa.
La situación con C ++:
c ++ 11
9.5 Uniones [class.union]
En una unión, como máximo uno de los miembros de datos no estáticos puede estar activo en cualquier momento, es decir, el valor de como máximo uno de los miembros de datos no estáticos puede almacenarse en una unión en cualquier momento.
C ++ más tarde tiene lenguaje que permite el uso de uniones que contienen struct
s con secuencias iniciales comunes; Sin embargo, esto no permite la escritura de tipos.
Para determinar si la unión de tipos está permitida en C ++, tenemos que buscar más. Recordar quec99 es una referencia normativa para C ++ 11 (y C99 tiene un lenguaje similar al C11 que permite la unión de tipos):
3.9 Tipos [tipos.básicos]
4 - La representación de objeto de un objeto de tipo T es la secuencia de N objetos de caracteres sin signo tomados por el objeto de tipo T, donde N es igual a sizeof (T). La representación del valor de un objeto es el conjunto de bits que contienen el valor del tipo T. Para los tipos que se pueden copiar trivialmente, la representación del valor es un conjunto de bits en la representación del objeto que determina un valor, que es un elemento discreto de una implementación. conjunto de valores definido. 42
42) La intención es que el modelo de memoria de C ++ sea compatible con el del lenguaje de programación ISO / IEC 9899 C.
Se pone particularmente interesante cuando leemos
3.8 Vida útil del objeto [basic.life]
La vida útil de un objeto de tipo T comienza cuando: - se obtiene el almacenamiento con la alineación y el tamaño adecuados para el tipo T, y - si el objeto tiene una inicialización no trivial, su inicialización está completa.
Entonces, para un tipo primitivo (que ipso facto tiene una inicialización trivial) contenido en una unión, la vida útil del objeto abarca al menos la vida útil de la unión misma. Esto nos permite invocar
3.9.2 Tipos de compuestos [basic.compound]
Si un objeto de tipo T se encuentra en una dirección A, se dice que un puntero de tipo cv T * cuyo valor es la dirección A apunta a ese objeto, independientemente de cómo se obtuvo el valor.
Suponiendo que la operación en la que estamos interesados es la escritura de tipos, es decir, tomar el valor de un miembro de la unión no activo, y dado lo anterior que tenemos una referencia válida al objeto al que hace referencia ese miembro, esa operación es lvalue-to -conversión de valor:
4.1 Conversión Lvalue-to-rvalue [conv.lval]
Un valor de gl de un tipo sin función, sin matriz T
se puede convertir en un valor pr. Si T
es un tipo incompleto, un programa que necesita esta conversión está mal formado. Si el objeto al que se refiere el valor gl no es un objeto de tipo T
y no es un objeto de un tipo derivado de T
, o si el objeto no está inicializado, un programa que necesita esta conversión tiene un comportamiento indefinido.
La pregunta entonces es si un objeto que es un miembro de la unión no activo se inicializa por almacenamiento en el miembro de la unión activa. Por lo que puedo decir, este no es el caso y, aunque si:
- una unión se copia en el
char
almacenamiento de matriz y viceversa (3.9: 2), o
- una unión se copia por byte en otra unión del mismo tipo (3.9: 3), o
- Se accede a una unión a través de los límites del idioma mediante un elemento del programa conforme a ISO / IEC 9899 (hasta donde se define) (3.9: 4 nota 42), luego
el acceso a una unión por un miembro no activo se define y se define para seguir la representación del objeto y valor, el acceso sin una de las interposiciones anteriores es un comportamiento indefinido. Esto tiene implicaciones para las optimizaciones permitidas en dicho programa, ya que la implementación puede, por supuesto, asumir que no ocurre un comportamiento indefinido.
Es decir, aunque podemos formar legítimamente un valor l para un miembro sindical no activo (razón por la cual está bien asignar a un miembro no activo sin construcción) se considera no inicializado.