La confusion est que C autorise explicitement le poinçonnage de type via une union, alors que C ++ (c ++ 11) n'a pas une telle autorisation.
c11
6.5.2.3 Structure et membres du syndicat
95) Si le membre utilisé pour lire le contenu d'un objet union n'est pas le même que le dernier membre utilisé pour stocker une valeur dans l'objet, la partie appropriée de la représentation d'objet de la valeur est réinterprétée comme une représentation d'objet dans le nouveau tapez comme décrit dans 6.2.6 (un processus parfois appelé «poinçonnage de type»). Cela pourrait être une représentation piège.
La situation avec C ++:
c ++ 11
9.5 Unions [class.union]
Dans une union, au plus l'un des membres de données non statiques peut être actif à tout moment, c'est-à-dire que la valeur d'au plus l'un des membres de données non statiques peut être stockée dans une union à tout moment.
C ++ a plus tard un langage permettant l'utilisation d'unions contenant des struct
s avec des séquences initiales communes; cela ne permet cependant pas le poinçonnage de type.
Pour déterminer si la punition de type d'union est autorisé en C ++, nous devons chercher plus loin. Rappeler quec99 est une référence normative pour C ++ 11 (et C99 a un langage similaire à C11 permettant l'union de type-punning):
3.9 Types [basic.types]
4 - La représentation objet d'un objet de type T est la suite de N objets char non signés repris par l'objet de type T, où N est égal à sizeof (T). La représentation de valeur d'un objet est l'ensemble de bits qui contiennent la valeur de type T.Pour les types trivialement copiables, la représentation de valeur est un ensemble de bits dans la représentation d'objet qui détermine une valeur, qui est un élément discret d'une implémentation. ensemble de valeurs défini. 42
42) L'intention est que le modèle de mémoire de C ++ soit compatible avec celui du langage de programmation ISO / CEI 9899 C.
Cela devient particulièrement intéressant quand on lit
3.8 Durée de vie de l'objet [basic.life]
La durée de vie d'un objet de type T commence lorsque: - le stockage avec l'alignement et la taille appropriés pour le type T est obtenu, et - si l'objet a une initialisation non triviale, son initialisation est terminée.
Ainsi, pour un type primitif (qui a ipso facto une initialisation triviale) contenu dans une union, la durée de vie de l'objet englobe au moins la durée de vie de l'union elle-même. Cela nous permet d'invoquer
3.9.2 Types composés [basic.compound]
Si un objet de type T se trouve à une adresse A, on dit qu'un pointeur de type cv T * dont la valeur est l'adresse A pointe vers cet objet, quelle que soit la manière dont la valeur a été obtenue.
En supposant que l'opération qui nous intéresse est le poinçonnage de type, c'est-à-dire qu'elle prend la valeur d'un membre syndical non actif, et que, conformément à ce qui précède, nous avons une référence valide à l'objet auquel ce membre fait référence, cette opération est lvalue-to -rvaleur conversion:
4.1 Conversion Lvalue-to-rvalue [conv.lval]
Une glvalue d'un type non-function, non-array T
peut être convertie en prvalue. Si T
est un type incomplet, un programme qui nécessite cette conversion est mal formé. Si l'objet auquel se réfère la valeur de glissement n'est pas un objet de type T
et n'est pas un objet d'un type dérivé de T
, ou si l'objet n'est pas initialisé, un programme qui nécessite cette conversion a un comportement non défini.
La question est alors de savoir si un objet qui est un membre syndical non actif est initialisé par stockage sur le membre syndical actif. Pour autant que je sache, ce n'est pas le cas et donc si:
- une union est copiée dans le
char
stockage de la baie et inversement (3.9: 2), ou
- une union est copiée par octet dans une autre union du même type (3.9: 3), ou
- une union est accessible au-delà des frontières linguistiques par un élément de programme conforme à ISO / CEI 9899 (pour autant que cela soit défini) (3.9: 4 note 42), alors
l'accès à une union par un membre non actif est défini et est défini pour suivre la représentation de l'objet et de la valeur, l'accès sans l'une des interpositions ci-dessus est un comportement indéfini. Cela a des implications pour les optimisations autorisées à être effectuées sur un tel programme, car l'implémentation peut bien sûr supposer qu'un comportement indéfini ne se produit pas.
Autrement dit, bien que nous puissions légitimement former une lvalue à un membre syndical non actif (c'est pourquoi l'attribution à un membre non actif sans construction est acceptable), elle est considérée comme non initialisée.