reinterpret_cast
conversion
Convertit entre les types en réinterprétant le motif de bits sous-jacent.
Table des matières |
Syntaxe
reinterpret_cast<
type-cible
>(
expression
)
|
|||||||||
Retourne une valeur de type target-type .
Explication
Contrairement à static_cast , mais comme const_cast , l'expression reinterpret_cast ne compile en aucune instruction CPU (sauf lors de conversions entre entiers et pointeurs, ou entre pointeurs sur des architectures obscures où la représentation des pointeurs dépend de leur type). Il s'agit principalement d'une directive à la compilation qui indique au compilateur de traiter expression comme s'il avait le type target-type .
Seules les conversions suivantes peuvent être effectuées avec reinterpret_cast , sauf lorsque ces conversions supprimeraient la constance (ou la volatilité).
static_cast
ou
conversion implicite
devrait être utilisé à cette fin.
|
4)
Toute valeur de type
std::nullptr_t
, y compris
nullptr
, peut être convertie en n'importe quel type intégral comme si c'était
(
void
*
)
0
, mais aucune valeur, pas même
nullptr
, ne peut être convertie en
std::nullptr_t
:
static_cast
doit être utilisé à cet effet.
|
(depuis C++11) |
T1*
peut être converti en un autre type de pointeur d'objet
cv
T2*
. Ceci est exactement équivalent à
static_cast
<
cv
T2
*
>
(
static_cast
<
cv
void
*
>
(
expression
)
)
(ce qui implique que si l'exigence d'alignement de
T2
n'est pas plus stricte que celle de
T1
, la valeur du pointeur ne change pas et la conversion du pointeur résultant vers son type original redonne la valeur originale). Dans tous les cas, le pointeur résultant ne peut être déréférencé de manière sûre que si la valeur déréférencée est
accessible par le type
.
T1
peut être convertie en référence vers un autre type
T2
. Le résultat est celui de
*
reinterpret_cast
<
T2
*
>
(
p
)
, où
p
est un pointeur de type « pointeur vers
T1
» vers l'objet ou la fonction désigné(e) par
expression
. Aucun temporaire n'est
matérialisé ou
(depuis C++17)
créé, aucune copie n'est effectuée, aucun constructeur ou fonction de conversion n'est appelé. La référence résultante ne peut être accédée en toute sécurité que si elle est
accessible par le type
.
dlsym
), un pointeur de fonction peut être converti en
void
*
ou tout autre pointeur d'objet, ou vice versa. Si l'implémentation prend en charge la conversion dans les deux sens, la conversion vers le type original produit la valeur originale, sinon le pointeur résultant ne peut pas être déréférencé ou appelé en toute sécurité.
T1
peut être converti en un pointeur vers un autre membre objet d'une autre classe
T2
. Si l'alignement de
T2
n'est pas plus strict que celui de
T1
, la reconversion vers le type original
T1
produit la valeur originale, sinon le pointeur résultant ne peut pas être utilisé en toute sécurité.
Comme pour toutes les expressions de cast, le résultat est :
- un lvalue si target-type est un type référence lvalue ou une référence rvalue vers un type fonction (depuis C++11) ;
|
(depuis C++11) |
- un prvalue sinon.
Alias de type
Accessibilité des types
Si un type
T_ref
est
similaire
à l'un des types suivants, un objet de
type dynamique
T_obj
est
accessible par type
via une
lvalue
(jusqu'en C++11)
glvalue
(depuis C++11)
de type
T_ref
:
- char , unsigned char ou std::byte (depuis C++17) : cela permet d'examiner la représentation objet de tout objet comme un tableau d'octets.
-
T_obj -
le type signé ou non signé correspondant à
T_obj
Si un programme tente de lire ou de modifier la valeur stockée d'un objet via un lvalue (jusqu'à C++11) glvalue (depuis C++11) via lequel il n'est pas accessible par le type, le comportement est indéfini.
Cette règle active l'analyse d'alias basée sur les types, dans laquelle un compilateur suppose que la valeur lue via une glvalue d'un type n'est pas modifiée par une écriture vers une glvalue d'un type différent (sous réserve des exceptions mentionnées ci-dessus).
Notez que de nombreux compilateurs C++ assouplissent cette règle, via une extension de langage non standard, pour autoriser l'accès avec un type incorrect via le membre inactif d'une union (un tel accès n'est pas indéfini en C).
Compatibilité des appels
Si l'une des conditions suivantes est satisfaite, un type
T_call
est
compatible d'appel
avec un type de fonction
T_func
:
-
T_callest du même type queT_func.
|
(depuis C++17) |
Si une fonction est appelée via une expression dont le type de fonction n'est pas compatible avec le type de la définition de la fonction appelée, le comportement est indéfini.
Notes
En supposant que les exigences d'alignement soient satisfaites, un reinterpret_cast ne modifie pas la valeur d'un pointeur en dehors de quelques cas limités traitant des objets pointer-interconvertible :
struct S1 { int a; } s1; struct S2 { int a; private: int b; } s2; // pas standard-layout union U { int a; double b; } u = {0}; int arr[2]; int* p1 = reinterpret_cast<int*>(&s1); // la valeur de p1 est "pointeur vers s1.a" car // s1.a et s1 sont interchangeables par pointeur int* p2 = reinterpret_cast<int*>(&s2); // la valeur de p2 est inchangée par reinterpret_cast // et est "pointeur vers s2". int* p3 = reinterpret_cast<int*>(&u); // la valeur de p3 est "pointeur vers u.a" : // u.a et u sont interchangeables par pointeur double* p4 = reinterpret_cast<double*>(p3); // la valeur de p4 est "pointeur vers u.b" : u.a et // u.b sont interchangeables par pointeur car // les deux sont interchangeables par pointeur avec u int* p5 = reinterpret_cast<int*>(&arr); // la valeur de p5 est inchangée par reinterpret_cast // et est "pointeur vers arr"
Effectuer un accès à un membre de classe qui désigne un membre de données non statique ou une fonction membre non statique sur une glvalue qui ne désigne pas réellement un objet du type approprié - tel que celui obtenu via un reinterpret_cast - entraîne un comportement indéfini :
struct S { int x; }; struct T { int x; int f(); }; struct S1 : S {}; // disposition standard struct ST : S, T {}; // pas de disposition standard S s = {}; auto p = reinterpret_cast<T*>(&s); // la valeur de p est "pointeur vers s" auto i = p->x; // l'expression d'accès au membre de classe est un comportement indéfini ; // s n'est pas un objet T p->x = 1; // comportement indéfini p->f(); // comportement indéfini S1 s1 = {}; auto p1 = reinterpret_cast<S*>(&s1); // la valeur de p1 est "pointeur vers le sous-objet S de s1" auto i = p1->x; // OK p1->x = 1; // OK ST st = {}; auto p2 = reinterpret_cast<S*>(&st); // la valeur de p2 est "pointeur vers st" auto i = p2->x; // comportement indéfini p2->x = 1; // comportement indéfini
De nombreux compilateurs émettent des avertissements de "strict aliasing" dans de tels cas, même si techniquement ces constructions enfreignent autre chose que le paragraphe communément appelé la "strict aliasing rule".
Le but des règles de l'aliasing strict et des règles associées est de permettre l'analyse d'alias basée sur les types, qui serait anéantie si un programme pouvait valablement créer une situation où deux pointeurs vers des types non liés (par exemple, un int * et un float * ) pourraient exister simultanément et que tous deux puissent être utilisés pour charger ou stocker la même mémoire (voir cet email sur le réflecteur SG12 ). Par conséquent, toute technique semblant capable de créer une telle situation invoque nécessairement un comportement indéfini.
Lorsqu'il est nécessaire d'interpréter les octets d'un objet comme une valeur d'un type différent, std::memcpy ou std::bit_cast (depuis C++20) peut être utilisé :
double d = 0.1; std::int64_t n; static_assert(sizeof n == sizeof d); // n = *reinterpret_cast<std::int64_t*>(&d); // Comportement indéfini std::memcpy(&n, &d, sizeof d); // OK n = std::bit_cast<std::int64_t>(d); // également OK
|
Si l'implémentation fournit std::intptr_t et/ou std::uintptr_t , alors une conversion d'un pointeur vers un type objet ou cv void vers ces types est toujours bien définie. Cependant, ceci n'est pas garanti pour un pointeur de fonction. |
(depuis C++11) |
En C, la copie et l'assignation d'agrégats accèdent à l'objet agrégé dans son ensemble. Mais en C++, de telles actions sont toujours effectuées via un appel de fonction membre, qui accède aux sous-objets individuels plutôt qu'à l'objet entier (ou, dans le cas des unions, copie la représentation de l'objet, c'est-à-dire via unsigned char ).
Mots-clés
Exemple
Illustre quelques utilisations de reinterpret_cast :
#include <cassert> #include <cstdint> #include <iostream> int f() { return 42; } int main() { int i = 7; // pointeur vers entier et retour std::uintptr_t v1 = reinterpret_cast<std::uintptr_t>(&i); // static_cast est une erreur std::cout << "The value of &i is " << std::showbase << std::hex << v1 << '\n'; int* p1 = reinterpret_cast<int*>(v1); assert(p1 == &i); // pointeur de fonction vers un autre et retour void(*fp1)() = reinterpret_cast<void(*)()>(f); // fp1(); comportement indéfini int(*fp2)() = reinterpret_cast<int(*)()>(fp1); std::cout << std::dec << fp2() << '\n'; // sûr // aliasing de type via pointeur char* p2 = reinterpret_cast<char*>(&i); std::cout << (p2[0] == '\x7' ? "This system is little-endian\n" : "This system is big-endian\n"); // aliasing de type via référence reinterpret_cast<unsigned int&>(i) = 42; std::cout << i << '\n'; [[maybe_unused]] const int &const_iref = i; // int &iref = reinterpret_cast<int&>( // const_iref); // erreur de compilation - impossible de supprimer const // Doit utiliser const_cast à la place : int &iref = const_cast<int&>(const_iref); }
Sortie possible :
The value of &i is 0x7fff352c3580 42 This system is little-endian 42
Rapports de défauts
Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.
| DR | Appliqué à | Comportement tel que publié | Comportement correct |
|---|---|---|---|
| CWG 195 | C++98 |
conversion entre pointeurs de fonction
et pointeurs d'objet non autorisée |
rendue conditionnellement supportée |
| CWG 658 | C++98 |
le résultat des conversions de pointeurs était non spécifié
(sauf pour les conversions vers le type d'origine) |
spécification fournie pour les pointeurs
dont les types pointés satisfont les exigences d'alignement |
| CWG 799 | C++98 |
il n'était pas clair quelle conversion d'identité
pouvait être effectuée par reinterpret_cast |
clarifié |
| CWG 1268 | C++11 |
reinterpret_cast
ne pouvait convertir que
des lvalues en types référence |
xvalues également autorisés |
| CWG 2780 | C++98 |
reinterpret_cast
ne pouvait pas convertir
des lvalues de fonction en d'autres types référence |
autorisé |
| CWG 2939 | C++17 |
reinterpret_cast
pouvait convertir
des prvalues en types référence rvalue |
non autorisé |
Références
- Norme C++23 (ISO/IEC 14882:2024) :
-
- 7.6.1.10 Conversion réinterprétée [expr.reinterpret.cast]
- Norme C++20 (ISO/CEI 14882:2020) :
-
- 7.6.1.9 Conversion réinterprétée [expr.reinterpret.cast]
- Norme C++17 (ISO/IEC 14882:2017) :
-
- 8.2.10 Conversion reinterpretée [expr.reinterpret.cast]
- Norme C++14 (ISO/IEC 14882:2014) :
-
- 5.2.10 Conversion reinterpretée [expr.reinterpret.cast]
- Norme C++11 (ISO/IEC 14882:2011) :
-
- 5.2.10 Conversion réinterprétée [expr.reinterpret.cast]
- Norme C++98 (ISO/CEI 14882:1998) :
-
- 5.2.10 Conversion reinterpretée [expr.reinterpret.cast]
- Norme C++03 (ISO/CEI 14882:2003) :
-
- 5.2.10 Conversion reinterpretée [expr.reinterpret.cast]
Voir aussi
const_cast
conversion
|
ajoute ou supprime const |
static_cast
conversion
|
effectue des conversions de base |
dynamic_cast
conversion
|
effectue des conversions polymorphes vérifiées |
| casts explicites | conversions permissives entre types |
| conversions standard | conversions implicites d'un type à un autre |
|
(C++20)
|
réinterprète la représentation objet d'un type comme celle d'un autre
(modèle de fonction) |