Quand utiliser reinterpret_cast?

Je suis un peu confus avec l’applicabilité de static_cast vs static_cast . D’après ce que j’ai lu, les règles générales consistent à utiliser le cast statique lorsque les types peuvent être interprétés au moment de la compilation, d’où le mot static . C’est la dissortingbution que le compilateur C ++ utilise en interne pour les castings implicites également.

reinterpret_cast s sont applicables dans deux scénarios, convertissent les types entiers en types de pointeurs et inversement ou convertissent un type de pointeur en un autre. L’idée générale que j’ai est que c’est inacceptable et doit être évité.

Là où je suis un peu confus, il y a une utilisation dont j’ai besoin, j’appelle C ++ depuis C et le code C doit conserver l’object C ++. Quel casting devrait être utilisé pour convertir entre le void * et le type de classe?

J’ai vu l’utilisation des deux static_cast et static_cast ? Bien que d’après ce que j’ai lu, la static semble meilleure car la dissortingbution peut se produire à la compilation? Bien qu’il soit dit d’utiliser reinterpret_cast pour convertir d’un type de pointeur à un autre?

Le standard C ++ garantit les éléments suivants:

static_cast un pointeur vers et depuis void* préserve l’adresse. C’est-à-dire que, dans ce qui suit, a, b et c indiquent tous la même adresse:

 int* a = new int(); void* b = static_cast(a); int* c = static_cast(b); 

reinterpret_cast garantit uniquement que si vous convertissez un pointeur sur un autre type et que vous le reinterpret_cast ensuite , vous récupérerez la valeur d’origine. Donc dans la suite:

 int* a = new int(); void* b = reinterpret_cast(a); int* c = reinterpret_cast(b); 

a et c contiennent la même valeur, mais la valeur de b n’est pas spécifiée. (en pratique, il contient généralement la même adresse que a et c, mais ce n’est pas spécifié dans la norme, et cela peut ne pas être vrai sur les machines avec des systèmes de mémoire plus complexes.)

Pour le casting vers et depuis le vide *, static_cast devrait être préféré.

Un cas où reinterpret_cast est nécessaire, c’est lors de l’interfaçage avec des types de données opaques. Cela se produit fréquemment dans les API du fournisseur sur lesquelles le programmeur n’a aucun contrôle. Voici un exemple inventif où un fournisseur fournit une API pour stocker et récupérer des données globales arbitraires:

 // vendor.hpp typedef struct _Opaque * VendorGlobalUserData; void VendorSetUserData(VendorGlobalUserData p); VendorGlobalUserData VendorGetUserData(); 

Pour utiliser cette API, le programmeur doit VendorGlobalUserData leurs données en VendorGlobalUserData et VendorGlobalUserData . static_cast ne fonctionnera pas, il faut utiliser reinterpret_cast :

 // main.cpp #include "vendor.hpp" #include  using namespace std; struct MyUserData { MyUserData() : m(42) {} int m; }; int main() { MyUserData u; // store global data VendorGlobalUserData d1; // d1 = &u; // comstack error // d1 = static_cast(&u); // comstack error d1 = reinterpret_cast(&u); // ok VendorSetUserData(d1); // do other stuff... // resortingeve global data VendorGlobalUserData d2 = VendorGetUserData(); MyUserData * p = 0; // p = d2; // comstack error // p = static_cast(d2); // comstack error p = reinterpret_cast(d2); // ok if (p) { cout << p->m << endl; } return 0; } 

Vous trouverez ci-dessous une implémentation de l’API exemple:

 // vendor.cpp static VendorGlobalUserData g = 0; void VendorSetUserData(VendorGlobalUserData p) { g = p; } VendorGlobalUserData VendorGetUserData() { return g; } 

La réponse courte: Si vous ne savez pas ce que reinterpret_cast représente, ne l’utilisez pas. Si vous en avez besoin à l’avenir, vous le saurez.

Réponse complète:

Considérons les types de base.

Lorsque vous convertissez par exemple int(12) en unsigned float (12.0f) votre processeur doit appeler certains calculs car les deux nombres ont une représentation de bits différente. C’est ce que static_cast représente.

D’autre part, lorsque vous appelez reinterpret_cast le processeur n’invoque aucun calcul. Il traite simplement un ensemble de bits dans la mémoire comme s’il avait un autre type. Ainsi, lorsque vous convertissez int* en float* avec ce mot-clé, la nouvelle valeur (après le déréférencement du pointeur) n’a rien à voir avec l’ancienne valeur au sens mathématique.

Exemple: Il est vrai que reinterpret_cast n’est pas portable à cause d’un ordre de raison (endianness). Mais c’est souvent la meilleure raison de l’utiliser. Imaginons l’exemple: vous devez lire le numéro binary 32bit du fichier et vous savez qu’il est gros. Votre code doit être générique et fonctionne correctement sur les systèmes big endian (par exemple, ARM) et little endian (par exemple x86). Donc, vous devez vérifier l’ordre des octets. Il est bien connu au moment de la compilation, vous pouvez donc écrire la fonction constexpr :

 constexpr bool is_little_endian() { std::uint16_t x=0x0001; auto p = reinterpret_cast(&x); return *p != 0; } 

Explication: la représentation binary de x en mémoire peut être 0000'0000'0000'0001 (big) ou 0000'0001'0000'0000 (little endian). Après réinterprétation-diffusion, l’octet sous le pointeur p pourrait être respectivement 0000'0000 ou 0000'0001 . Si vous utilisez le 0000'0001 statique, il sera toujours 0000'0001 , quel que soit l’ 0000'0001 utilisé.

La signification de reinterpret_cast n’est pas définie par le standard C ++. Par conséquent, en théorie, un reinterpret_cast pourrait faire planter votre programme. En pratique, les compilateurs essaient de faire ce que vous attendez, c’est-à-dire d’interpréter les bits de ce que vous transmettez comme s’ils étaient du type auquel vous voulez le faire. Si vous savez ce que les compilateurs que vous allez utiliser avec reinterpret_cast vous pouvez l’utiliser, mais dire que c’est portable est mentir.

Pour le cas que vous décrivez, et à peu près tous les cas où vous pourriez envisager static_cast , vous pouvez utiliser static_cast ou une autre alternative. Entre autres choses, le standard a ceci à dire sur ce que vous pouvez attendre de static_cast (§5.2.9):

Une valeur de type “pointeur sur cv void” peut être explicitement convertie en un pointeur sur un type d’object. Une valeur de type pointeur sur un object converti en «pointeur sur cv void» et renvoyée au type de pointeur d’origine aura sa valeur d’origine.

Donc, pour votre cas d’utilisation, il semble assez clair que le comité de normalisation a voulu que vous static_cast .

Une des utilisations de reinterpret_cast est si vous souhaitez appliquer des opérations au niveau du bit (IEEE 754). Un exemple de ceci était l’astuce rapide carrée de racine inverse:

https://en.wikipedia.org/wiki/Fast_inverse_square_root#Overview_of_the_code

Il traite la représentation binary du flottant comme un entier, le décale à droite et le soustrait d’une constante, réduisant ainsi de moitié et annulant l’exposant. Après reconversion en flotteur, il est soumis à une itération de Newton-Raphson pour rendre cette approximation plus précise:

 float Q_rsqrt( float number ) { long i; float x2, y; const float threehalfs = 1.5F; x2 = number * 0.5F; y = number; i = * ( long * ) &y; // evil floating point bit level hacking i = 0x5f3759df - ( i >> 1 ); // what the deuce? y = * ( float * ) &i; y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration // y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed return y; } 

Ceci a été écrit à l’origine en C, utilise donc C cast, mais le casting C ++ analogue est reinterpret_cast.

Vous pouvez utiliser reinterprete_cast pour vérifier l’inheritance au moment de la compilation.
Regardez ici: Utilisation de reinterpret_cast pour vérifier l’inheritance au moment de la compilation

 template  outType safe_cast(inType pointer) { void* temp = static_cast(pointer); return static_cast(temp); } 

J’ai essayé de conclure et j’ai écrit une dissortingbution simple en utilisant des modèles. Notez que cette solution ne garantit pas de lancer des pointeurs sur une fonction.

Tout d’abord, vous avez des données dans un type spécifique comme int ici:

 int x = 0x7fffffff://==nan in binary representation 

Ensuite, vous voulez accéder à la même variable qu’un autre type comme float: vous pouvez choisir entre

 float y = reinterpret_cast(x); //this could only be used in cpp, looks like a function with template-parameters 

ou

 float y = *(float*)&(x); //this could be used in c and cpp 

BRIEF: cela signifie que la même mémoire est utilisée comme un type différent. Donc, vous pouvez convertir des représentations binarys de flottants en type int comme ci-dessus en flottants. 0x80000000 est par exemple -0 (la mantisse et l’exposant sont nuls, mais le signe, le msb, est un. Cela fonctionne également pour les doubles doubles et longs.

OPTIMISER: Je pense que reinterpret_cast serait optimisé dans de nombreux compilateurs, tandis que le c-casting est fait par pointerarithmetic (la valeur doit être copiée dans la mémoire, car les pointeurs ne peuvent pas pointer vers les cpu-registres).

REMARQUE: Dans les deux cas, vous devez enregistrer la valeur convertie dans une variable avant la dissortingbution! Cette macro pourrait aider:

 #define asvar(x) ({decltype(x) __tmp__ = (x); __tmp__; }) 

Réponse rapide: utilisez static_cast si elle comstack, sinon utilisez static_cast .

Lisez la FAQ ! Tenir des données C ++ en C peut être risqué.

En C ++, un pointeur vers un object peut être converti en void * sans aucune projection. Mais ce n’est pas vrai à l’inverse. Vous aurez besoin d’un static_cast pour récupérer le pointeur d’origine.