¿Debo usar static_cast o reinterpret_cast al lanzar un vacío * a lo que sea

202

Parece que tanto static_cast como reinterpret_cast funcionan bien para convertir void * a otro tipo de puntero. ¿Hay una buena razón para favorecer a uno sobre el otro?

Andy
fuente
78
@anon Aparentemente nunca antes habías trabajado con hilos POSIX.
user470379
77
@ user470379 Wow ... esa es la razón por la que llegué a esta pregunta en SO! Excelente observación :-).
Ogre Psalm33

Respuestas:

148

Usostatic_cast : es el modelo más estrecho que describe exactamente qué conversión se realiza aquí.

Existe la idea errónea de que usar reinterpret_castsería una mejor combinación porque significa "ignorar completamente la seguridad de tipo y simplemente lanzar de A a B".

Sin embargo, esto en realidad no describe el efecto de a reinterpret_cast. Más bien, reinterpret_casttiene una serie de significados, para todos los cuales sostiene que "el mapeo realizado por reinterpret_castestá definido por la implementación". [5.2.10.3]

Pero en el caso particular de echar de void*aT* la cartografía es completamente bien definido por la norma; a saber, asignar un tipo a un puntero sin tipo sin cambiar su dirección.

Esta es una razón para preferir static_cast.

Además, y posiblemente más importante, es el hecho de que cada uso de reinterpret_castes francamente peligroso porque convierte cualquier cosa en otra cosa realmente (para punteros), mientras que static_castes mucho más restrictivo, proporcionando así un mejor nivel de protección. Esto ya me ha salvado de errores en los que accidentalmente intenté forzar un tipo de puntero a otro.

Konrad Rudolph
fuente
8

Esta es una pregunta difícil. Por un lado, Konrad hace un excelente comentario sobre la definición de especificaciones para reinterpret_cast , aunque en la práctica probablemente haga lo mismo. Por otro lado, si está convirtiendo entre tipos de puntero (como es bastante común al indexar en la memoria a través de un char *, por ejemplo), static_cast generará un error de compilador y se verá obligado a usar reinterpret_cast todos modos.

En la práctica, uso reinterpret_cast porque es más descriptivo de la intención de la operación de transmisión. Ciertamente, podría hacer un caso para que un operador diferente designe reinterpretaciones de puntero solamente (lo que garantiza la misma dirección devuelta), pero no hay una en el estándar.

Mella
fuente
66
" un operador diferente para designar el puntero reinterpreta solamente (lo que garantiza la misma dirección devuelta) " ¿Abrazo? Ese operador es reinterpret_cast !
curioso
2
@curiousguy No es cierto según el estándar. reinterpret_cast NO garantiza que se use la misma dirección. Solo que si reinterpreta_cast de un tipo a otro y luego regresa , obtendrá la misma dirección con la que comenzó.
ClydeTheGhost
0

Sugiero usar siempre el molde más débil posible.

reinterpret_castse puede usar para lanzar un puntero a a float. Cuanto más se rompe la estructura del elenco, más atención requiere usarlo.

En caso de char*, usaría el molde de estilo c, hasta que tengamos algo reinterpret_pointer_cast, porque es más débil y nada más es suficiente.

Pavel Radzivilovsky
fuente
2
" reinterpret_cast se puede usar para lanzar un puntero a un flotador " . ¡Ciertamente no!
curioso
3
Probablementefloat f = *reinterpret_cast<const float*>(&p);
Ben Voigt
2
@BenVoigt Eso es lanzar entre punteros; Uno de ellos resultó ser un puntero flotante.
nodakai
55
@BenVoigt, sin embargo, la "expresión completa" no es un elenco. La expresión consiste en una desreferencia aplicada a un molde. Afirmó que era posible lanzar un puntero a float, lo cual es falso. Los moldes de expresión void **a const float *, y luego utiliza una operación de eliminar la referencia (que no es un elenco), para convertir const float *a float.
MM
2
@BenVoigt le ofreció ese código en respuesta a otra pregunta "¿Cómo yo echo ...", y luego, cuando alguien dice que los elencos de código entre punteros (que lo hace), que dijeron "No"
MM
-7

Mi preferencia personal se basa en la alfabetización de código como esta:

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

o

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

Ambos hacen lo mismo al final, pero static_cast parece más apropiado en un entorno de aplicaciones de middleware, mientras que reinterpretar cast parece más como algo que verías en una biblioteca de nivel inferior en mi humilde opinión.

Robert Gould
fuente