Dois-je utiliser static_cast ou reinterpret_cast lors du casting d'un void *

202

Static_cast et reinterpret_cast semblent fonctionner correctement pour la conversion de void * en un autre type de pointeur. Y a-t-il une bonne raison de privilégier l'un plutôt que l'autre?

Andy
la source
78
@anon Apparemment, vous n'aviez jamais travaillé avec des threads POSIX auparavant.
user470379
7
@ user470379 Wow ... c'est la raison même pour laquelle j'ai atterri sur cette question à SO! Excellente observation :-).
Ogre Psalm33

Réponses:

148

Utilisationstatic_cast : c'est la distribution la plus étroite qui décrit exactement quelle conversion est effectuée ici.

Il y a une idée fausse selon laquelle l'utilisation reinterpret_castserait une meilleure correspondance, car cela signifie «ignorer complètement la sécurité de type et simplement lancer de A à B».

Cependant, cela ne décrit pas réellement l'effet d'un reinterpret_cast. Plutôt, reinterpret_casta un certain nombre de significations, pour toutes qui soutient que «le mappage effectué par reinterpret_castest défini par l' implémentation». [5.2.10.3]

Mais dans le cas particulier de la coulée de void*à T*la cartographie est complètement bien défini par la norme; à savoir, pour affecter un type à un pointeur sans type sans changer son adresse.

C'est une raison de préférer static_cast.

De plus, et sans doute plus important, est le fait que chaque utilisation de reinterpret_castest carrément dangereuse car elle convertit tout en n'importe quoi d'autre (pour les pointeurs), alors qu'elle static_castest beaucoup plus restrictive, offrant ainsi un meilleur niveau de protection. Cela m'a déjà sauvé des bogues où j'ai accidentellement essayé de forcer un type de pointeur dans un autre.

Konrad Rudolph
la source
8

C'est une question difficile. D'une part, Konrad fait un excellent point sur la définition de spécification pour reinterpret_cast , bien qu'en pratique cela fasse probablement la même chose. D'un autre côté, si vous effectuez un cast entre des types de pointeurs (comme cela est assez courant lors de l'indexation en mémoire via un char *, par exemple), static_cast générera une erreur de compilation et vous serez de toute façon obligé d'utiliser reinterpret_cast .

En pratique, j'utilise reinterpret_cast car il est plus descriptif de l'intention de l'opération de transtypage. Vous pourriez certainement plaider en faveur d'un opérateur différent pour désigner uniquement des réinterprétations de pointeurs (qui garantissaient la même adresse renvoyée), mais il n'y en a pas dans la norme.

pseudo
la source
6
" opérateur différent pour désigner uniquement les réinterprétations de pointeur (ce qui garantissait la même adresse renvoyée) " Hug? Cet opérateur est reinterpret_cast !
curiousguy
2
@curiousguy Pas vrai selon la norme. reinterpret_cast ne garantit PAS que la même adresse est utilisée. Seulement si vous réinterprétez_cast d'un type à un autre puis revenez , vous récupérerez la même adresse que celle avec laquelle vous avez commencé.
ClydeTheGhost
0

Je suggère d'utiliser toujours le casting le plus faible possible.

reinterpret_castpeut être utilisé pour lancer un pointeur sur a float. Plus le plâtre est cassant la structure, plus il faut d'attention pour l'utiliser.

Dans le cas char*contraire, j'utiliserais une fonte de style C, jusqu'à ce que nous en ayons reinterpret_pointer_cast, car elle est plus faible et rien d'autre ne suffit.

Pavel Radzivilovsky
la source
2
" reinterpret_cast peut être utilisé pour convertir un pointeur en un flotteur. " Certainement pas!
curiousguy
3
Probablementfloat f = *reinterpret_cast<const float*>(&p);
Ben Voigt
2
@BenVoigt C'est du cast entre des pointeurs; l'un d'eux était un pointeur flottant.
nodakai
5
@BenVoigt "l'expression entière" n'est cependant pas un casting. L'expression consiste en une déréférence appliquée à un casting. Vous avez affirmé qu'il était possible de placer un pointeur sur float, ce qui est faux. Les moulages d'expression void **à const float *, et utilise ensuite une opération de déréférencement ( ce qui est une distribution), pour convertir const float *à float.
MM
2
@BenVoigt, vous avez proposé ce code en réponse à quelqu'un qui vous a demandé "Comment puis-je caster ...", puis quand quelqu'un a dit que le code était jeté entre des pointeurs (ce qu'il fait), vous avez dit "Non"
MM
-7

Ma préférence personnelle est basée sur l'alphabétisation du code comme ceci:

void* data = something;
MyClass* foo = reinterpret_cast<MyClass*>(data);
foo->bar();

ou

typedef void* hMyClass; //typedef as a handle or reference
hMyClass = something;
const MyClass& foo = static_cast<MyClass&>(*hMyClass);
foo.bar();

Ils font tous les deux la même chose à la fin, mais static_cast semble plus approprié dans un environnement d'application intermédiaire, tandis que la réinterprétation de la distribution ressemble plus à quelque chose que vous verriez dans une bibliothèque de niveau inférieur à mon humble avis.

Robert Gould
la source