Namespaces
Variants

reinterpret_cast conversion

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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é).

1) Une expression de type intégral, d'énumération, de pointeur ou de pointeur-vers-membre peut être convertie en son propre type. La valeur résultante est identique à la valeur de l' expression .
2) Un pointeur peut être converti en n'importe quel type intégral suffisamment grand pour contenir toutes les valeurs de son type (par exemple en std::uintptr_t ).
3) Une valeur de tout type intégral ou énumération peut être convertie en un type pointeur. Un pointeur converti en un entier de taille suffisante et reconverti dans le même type pointeur est garanti d'avoir sa valeur d'origine, sinon le pointeur résultant ne peut pas être déréférencé en toute sécurité (la conversion aller-retour dans la direction opposée n'est pas garantie ; le même pointeur peut avoir plusieurs représentations entières). La constante de pointeur nul NULL ou le zéro entier ne garantit pas de produire la valeur de pointeur nul du type cible ; 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)
5) Tout type de pointeur d'objet 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 .
6) Une lvalue (jusqu'en C++11) glvalue (depuis C++11) expression de 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 .
7) Tout pointeur vers une fonction peut être converti en un pointeur vers un type de fonction différent. Le résultat n'est pas spécifié, mais reconvertir un tel pointeur en pointeur vers le type de fonction d'origine redonne le pointeur vers la fonction d'origine. Le pointeur résultant ne peut être appelé en toute sécurité que si son type de fonction est compatible d'appel avec le type de fonction d'origine.
8) Sur certaines implémentations (en particulier, sur tout système compatible POSIX comme requis par 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é.
9) La valeur du pointeur nul de tout type de pointeur peut être convertie en tout autre type de pointeur, ce qui donne la valeur du pointeur nul de ce type. Notez que la constante de pointeur nul nullptr ou toute autre valeur de type std::nullptr_t ne peut pas être convertie en pointeur avec reinterpret_cast : une conversion implicite ou static_cast devrait être utilisée à cette fin.
10) Un pointeur vers une fonction membre peut être converti en pointeur vers une fonction membre différente d'un type différent. La conversion vers le type d'origine redonne la valeur originale, sinon le pointeur résultant ne peut pas être utilisé en toute sécurité.
11) Un pointeur vers un membre objet d'une certaine classe 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) ;
  • une xvalue si target-type est une référence à rvalue vers un type objet ;
(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_call est du même type que T_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

reinterpret_cast

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)