Je suis un peu confus avec l'applicabilité de reinterpret_cast
vs static_cast
. D'après ce que j'ai lu, les règles générales consistent à utiliser une conversion statique lorsque les types peuvent être interprétés au moment de la compilation, d'où le mot static
. C'est le cast que le compilateur C ++ utilise également en interne pour les conversions implicites.
reinterpret_cast
s sont applicables dans deux scénarios:
- convertir des types entiers en types pointeurs et vice versa
- convertir un type de pointeur en un autre. L'idée générale que je me fais est que ce n'est pas transférable et doit être évité.
Là où je suis un peu confus, c'est une utilisation dont j'ai besoin, j'appelle C ++ à partir de C et le code C doit s'accrocher à l'objet C ++, donc en gros il contient un void*
. Quelle conversion doit être utilisée pour convertir entre le type void *
et le type Class?
J'ai vu l'utilisation des deux static_cast
et reinterpret_cast
? Bien que d'après ce que j'ai lu, il semble static
que la diffusion puisse être meilleure au moment de la compilation? Bien qu'il indique d'utiliser reinterpret_cast
pour convertir d'un type de pointeur à un autre?
reinterpret_cast
ne se produit pas au moment de l'exécution. Ce sont deux instructions de compilation. De en.cppreference.com/w/cpp/language/reinterpret_cast : "Contrairement à static_cast, mais comme const_cast, l'expression reinterpret_cast ne se compile dans aucune instruction CPU. Il s'agit purement d'une directive de compilation qui demande au compilateur de traiter la séquence de bits (représentation d'objet) d'expression comme si elle avait le type new_type. "Réponses:
La norme C ++ garantit les éléments suivants:
static_cast
un pointeur vers et depuisvoid*
conserve l’adresse. C'est-à-dire, dans ce qui suit,a
,b
etc
tous les points à la même adresse:reinterpret_cast
garantit uniquement que si vous transformez un pointeur en un autre type, puis enreinterpret_cast
le replaçant dans le type d'origine , vous obtenez la valeur d'origine. Donc, dans ce qui suit:a
etc
contiennent la même valeur, mais la valeur deb
n'est pas spécifiée. (dans la pratique, il contiendra généralement la même adresse quea
etc
, 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 la coulée vers et depuis
void*
,static_cast
doit être préférée.la source
b
n'est plus non spécifiée en C ++ 11 lors de l'utilisationreinterpret_cast
. Et en C ++ 03, il était interdit de faire un cast deint*
to (bien que les compilateurs ne l'aient pas implémenté et que cela ne soit pas pratique, il a donc été modifié pour C ++ 11).void*
reinterpret_cast
Un cas où cela
reinterpret_cast
est nécessaire est lors de l'interfaçage avec des types de données opaques. Cela se produit fréquemment dans les API des fournisseurs sur lesquelles le programmeur n'a aucun contrôle. Voici un exemple artificiel où un fournisseur fournit une API pour stocker et récupérer des données globales arbitraires:Pour utiliser cette API, le programmeur doit convertir et inverser ses données
VendorGlobalUserData
.static_cast
ne fonctionnera pas, il faut utiliserreinterpret_cast
:Voici une implémentation artificielle de l'exemple d'API:
la source
void*
-t-elle pas?USpoofChecker*
, oùUSpoofChecker
est une structure vide. Cependant, sous le capot, chaque fois que vous passez unUSpoofChecker*
, il subitreinterpret_cast
un type C ++ interne.La réponse courte: si vous ne savez pas ce que
reinterpret_cast
signifie, ne l'utilisez pas. Si vous en aurez besoin à l'avenir, vous le saurez.Réponse complète:
Prenons les types de nombres de base.
Lorsque vous convertissez par exemple
int(12)
àunsigned float (12.0f)
vos besoins de processeur pour appeler certains calculs que les deux nombres a une représentation différente de bits. Voilà ce questatic_cast
représente.D'un autre côté, lorsque vous appelez
reinterpret_cast
le CPU , il n'appelle aucun calcul. Il traite simplement un ensemble de bits dans la mémoire comme s'il avait un autre type. Ainsi, lorsque vous convertissezint*
enfloat*
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 ce
reinterpret_cast
n'est pas portable pour une raison - l'ordre des octets (endianness). Mais c'est souvent étonnamment la meilleure raison de l'utiliser. Imaginons l'exemple: vous devez lire un nombre binaire 32 bits à partir d'un fichier, et vous savez que c'est du big endian. Votre code doit être générique et fonctionne correctement sur les systèmes big endian (par exemple certains ARM) et little endian (par exemple x86). Vous devez donc vérifier l'ordre des octets.Il est bien connu au moment de la compilation, vous pouvez donc écrire uneVous pouvez écrire une fonction pour y parvenir:constexpr
fonction:Explication: la représentation binaire de
x
en mémoire peut être0000'0000'0000'0001
(grande) ou0000'0001'0000'0000
(peu endienne). Après la réinterprétation, l'octet sous lep
pointeur peut être respectivement0000'0000
ou0000'0001
. Si vous utilisez la conversion statique, elle le sera toujours0000'0001
, quelle que soit l'endianité utilisée.ÉDITER:
Dans la première version que j'ai fait par exemple la fonction
is_little_endian
d'êtreconstexpr
. Il compile bien sur le plus récent gcc (8.3.0) mais la norme dit qu'il est illégal. Le compilateur clang refuse de le compiler (ce qui est correct).la source
short
prend 16 bits en mémoire. Corrigée.La signification de
reinterpret_cast
n'est pas définie par la norme C ++. Par conséquent, en théorie, un programmereinterpret_cast
pourrait planter. Dans la pratique, les compilateurs essaient de faire ce que vous attendez, c'est-à-dire d'interpréter les morceaux de ce que vous transmettez comme s'ils étaient du type vers lequel vous diffusez. Si vous savez ce que font les compilateurs que vous allez utiliser,reinterpret_cast
vous pouvez l’utiliser, mais pour dire qu’il est portable serait mentir.Pour le cas que vous décrivez, et à peu près tous les cas où vous pourriez envisager
reinterpret_cast
, vous pouvez utiliserstatic_cast
ou une autre alternative à la place. Entre autres choses, la norme a ceci à dire sur ce à quoi vous pouvez vous attendrestatic_cast
(§5.2.9):Donc, pour votre cas d'utilisation, il semble assez clair que le comité de normalisation vous a destiné à utiliser
static_cast
.la source
reinterpret_crash
. Aucun bug de compilation ne m'empêchera de planter mon programme de réinterprétation. Je signalerai un bogue dès que possible! </template<class T, U> T reinterpret_crash(U a) { return *(T*)nullptr; }
Une utilisation de reinterpret_cast est si vous souhaitez appliquer des opérations au niveau du bit aux flottants (IEEE 754). Un exemple de ceci était l'astuce Fast Inverse Square-Root:
https://en.wikipedia.org/wiki/Fast_inverse_square_root#Overview_of_the_code
Il traite la représentation binaire du flottant comme un entier, le déplace vers la droite et le soustrait d'une constante, réduisant ainsi de moitié et annulant l'exposant. Après la reconversion en float, il est soumis à une itération de Newton-Raphson pour rendre cette approximation plus exacte:
Cela a été écrit à l'origine en C, donc utilise des transtypages en C, mais le cast C ++ analogue est le reinterpret_cast.
la source
error: invalid cast of an rvalue expression of type 'int64_t {aka long long int}' to type 'double&' reinterpret_cast<double&>((reinterpret_cast<int64_t&>(d) >> 1) + (1L << 61))
- ideone.com/6S4ijcreinterpret_cast
parmemcpy
, est-ce toujours UB?memcpy
serait certainement le rendre légal.Vous pouvez utiliser reinterprete_cast pour vérifier l'héritage au moment de la compilation.
Regardez ici: Utilisation de reinterpret_cast pour vérifier l'héritage au moment de la compilation
la source
J'ai essayé de conclure et j'ai écrit une distribution simple et sûre à l'aide de modèles. Notez que cette solution ne garantit pas de lancer des pointeurs sur une fonction.
la source
reinterpret_cast
déjà dans cette situation: "Un pointeur d'objet peut être explicitement converti en un pointeur d'objet d'un type différent. [72] Lorsqu'une valeurv
de type de pointeur d'objet est convertie en type de pointeur d'objet" pointer to cvT
", le résultat eststatic_cast<cv T*>(static_cast<cv void*>(v))
. " - N3797.c++2003
norme je peux pas trouver quereinterpret_cast
ne faitstatic_cast<cv T*>(static_cast<cv void*>(v))
C++03
c'étaitC++98
. Des tonnes de projets ont utilisé l'ancien C ++ au lieu du C. portable. Parfois, vous devez vous soucier de la portabilité. Par exemple, vous devez prendre en charge le même code sous Solaris, AIX, HPUX, Windows. En ce qui concerne la dépendance et la portabilité du compilateur, c'est difficile. Donc, un bon exemple d'introduction d'un enfer de portabilité est d'utiliser unreinterpret_cast
dans votre codeVous avez d'abord quelques données dans un type spécifique comme int ici:
Ensuite, vous voulez accéder à la même variable qu'un autre type comme float: vous pouvez choisir entre
ou
BREF: cela signifie que la même mémoire est utilisée comme un type différent. Vous pouvez donc convertir des représentations binaires de flottants de type int comme ci-dessus en flottants. 0x80000000 est -0 par exemple (la mantisse et l'exposant sont nuls mais le signe, le msb, est un. Cela fonctionne également pour les doubles et les doubles longs.
OPTIMISER: Je pense que reinterpret_cast serait optimisé dans de nombreux compilateurs, tandis que la conversion C est faite par pointerarithmétique (la valeur doit être copiée dans la mémoire, car les pointeurs ne pouvaient pas pointer vers les registres cpu).
REMARQUE: dans les deux cas, vous devez enregistrer la valeur convertie dans une variable avant la conversion! Cette macro pourrait aider:
la source
reinterpret_cast
formeint
defloat&
est un comportement non défini.Une raison d'utiliser
reinterpret_cast
est lorsqu'une classe de base n'a pas de table virtuelle, mais une classe dérivée en a. Dans ce cas,static_cast
etreinterpret_cast
entraînera des valeurs de pointeur différentes (ce serait le cas atypique mentionné par jalf ci-dessus ). Juste comme un avertissement, je ne dis pas que cela fait partie de la norme, mais la mise en œuvre de plusieurs compilateurs répandus.À titre d'exemple, prenez le code ci-dessous:
Qui produit quelque chose comme:
Dans tous les compilateurs que j'ai essayés (MSVC 2015 et 2017, clang 8.0.0, gcc 9.2, icc 19.0.1 - voir godbolt pour les 3 derniers ) le résultat du
static_cast
diffère de celui dureinterpret_cast
par 2 (4 pour MSVC). Le seul compilateur à avertir de la différence était clang, avec:Une dernière mise en garde est que si la classe de base n'a pas de membres de données (par exemple le
int i;
) alors clang, gcc et icc renvoient la même adresse pourreinterpret_cast
que pourstatic_cast
, alors que MSVC n'en a toujours pas.la source
Voici une variante du programme d'Avi Ginsburg qui illustre clairement la propriété
reinterpret_cast
mentionnée par Chris Luengo, flodin et cmdLP: que le compilateur traite l'emplacement pointé vers la mémoire comme s'il s'agissait d'un objet du nouveau type:Ce qui se traduit par une sortie comme celle-ci:
On peut voir que l'objet B est construit en mémoire en tant que données spécifiques à B d'abord, suivi par l'objet A incorporé. Le
static_cast
renvoie correctement l'adresse de l'objet A incorporé et le pointeur créé parstatic_cast
donne correctement la valeur du champ de données. Le pointeur généré par lesreinterpret_cast
friandisesb
emplacement de mémoire de comme s'il s'agissait d'un simple objet A, et donc lorsque le pointeur essaie d'obtenir le champ de données, il renvoie des données spécifiques à B comme s'il s'agissait du contenu de ce champ.Une utilisation de
reinterpret_cast
consiste à convertir un pointeur en un entier non signé (lorsque les pointeurs et les entiers non signés ont la même taille):int i;
unsigned int u = reinterpret_cast<unsigned int>(&i);
la source
Réponse rapide: utilisez
static_cast
s'il compile, sinon recourez àreinterpret_cast
.la source
Lisez la FAQ ! La conservation des données C ++ en C peut être risquée.
En C ++, un pointeur sur un objet peut être converti en
void *
sans transtypage. Mais ce n'est pas vrai dans l'autre sens. Vous auriez besoin d'unstatic_cast
pour récupérer le pointeur d'origine.la source