Pour les types pour lesquels une telle distribution est autorisée (par exemple, si T1
est un module de type et T2
est unsigned char
), l'approche avec static_cast
est bien définie par la Norme.
D'autre part, reinterpret_cast
est entièrement mise en œuvre définis par la seule garantie que vous obtenez pour elle est que vous pouvez jeter un type de pointeur pour tout autre type de pointeur, puis de nouveau, et vous obtiendrez la valeur d'origine; et aussi, vous pouvez le convertir en un type pointeur vers un type intégral assez grand pour contenir une valeur de pointeur (qui varie en fonction de la mise en œuvre, et ne doit plus exister du tout), puis de le jeter en arrière, et vous obtiendrez la valeur d'origine.
Pour être plus précis, je vais juste citer les parties pertinentes de la Norme, en mettant en évidence les parties importantes:
5.2.10[expr.réinterpréter.distribution]:
La cartographie réalisée par reinterpret_cast est mise en œuvre définies. [Note: il peut, ou non, de produire une représentation différente de la valeur d'origine.] ... Un pointeur vers un objet peut être explicitement converti en un pointeur vers un objet de type différent.) Sauf que la conversion d'une rvalue de type "pointeur vers T1" le type "pointeur vers T2" (où T1 et T2 sont des types d'objet et où l'alignement des exigences de T2 sont pas plus strictes que celles de T1) et le retour à son type d'origine des rendements de l'original de la valeur du pointeur, le résultat d'un tel pointeur de la conversion n'est pas spécifié.
Donc quelque chose comme ceci:
struct pod_t { int x; };
pod_t pod;
char* p = reinterpret_cast<char*>(&pod);
memset(p, 0, sizeof pod);
effectivement indéterminée.
Expliquant pourquoi static_cast
d'œuvres est un peu plus compliqué. Voici le code ci-dessus réécrit pour utiliser static_cast
je crois que c'est la garantie de toujours fonctionner comme prévu par la Norme:
struct pod_t { int x; };
pod_t pod;
char* p = static_cast<char*>(static_cast<void*>(&pod));
memset(p, 0, sizeof pod);
Encore une fois, permettez-moi de citer les articles de la Norme qui, ensemble, m'amènent à conclure que la ci-dessus doivent être portable:
3.9[de base.types]:
Pour un objet quelconque (autre qu'une classe de base sous-objet) de la nacelle de type T, si oui ou non l'objet titulaire d'un permis de valeur de type T, le sous-jacent octets (1.7) faisant l'objet peut être copié dans un tableau de char ou unsigned char. Si le contenu du tableau de char ou unsigned char est recopié dans l'objet, l'objet doit ensuite tenir sa valeur d'origine.
L'objet de la représentation d'un objet de type T est la séquence de N unsigned char objets pris par l'objet de type T, où N est égal à sizeof(T).
3.9.2[de base.composé]:
Les objets de cv qualifiés (3.9.3) ou cv-non qualifiés de type void*
(pointeur sur void), peut être utilisé pour pointer vers des objets de type inconnu. Un void*
doit être en mesure de tenir un pointeur d'objet. Un cv qualifiés ou cv-non qualifié (3.9.3) void*
ont la même représentation et de l'alignement des exigences de cv qualifiés ou cv-sans restriction char*
.
3.10[de base.lval]:
Si un programme tente d'accéder à la valeur d'un objet à travers une lvalue d'autre que l'un des types suivants le comportement est indéfini):
- ...
-
un char ou unsigned char type.
4.10[conv.ptr]:
Une rvalue de type "pointeur de cv T, où T est un type d'objet, peuvent être convertis en une rvalue de type "pointeur vers cv vide." Le résultat de la conversion d'un "pointeur de cv T" à un "pointeur de cv nul", pointe vers le début de l'emplacement de stockage où l'objet de type T réside, comme si l'objet est un objet dérivé de (1.8) de type T (qui est, non pas une classe de base sous-objet).
5.2.9[expr.statique.distribution]:
L'inverse de la conversion de la séquence (clause 4), autres que la lvalue-à-rvalue (4.1), array-topointer (4.2), la fonction de pointeur (4.3) et boolean (4.12) les conversions, peut être effectuée de manière explicite à l'aide static_cast.
[EDIT] d'autre part, nous avons ce bijou:
9.2[classe.mem]/17:
Un pointeur vers un POD-struct objet, convenablement converti à l'aide d'un reinterpret_cast, points à son premier membre (ou si le membre est un peu de champ, puis à l'unité dans laquelle il se trouve), et vice-versa. [Note: Il se peut donc être sans nom rembourrage à l'intérieur d'une GOUSSE de-struct objet, mais pas à son début, comme nécessaire afin d'obtenir l'alignement. ]
ce qui semble impliquer que, reinterpret_cast
entre pointeurs en quelque sorte implique "même adresse". Aller à la figure.