Estoy algo confundido con la aplicabilidad de reinterpret_cast
frente static_cast
. Por lo que he leído, las reglas generales son usar reparto estático cuando los tipos se pueden interpretar en tiempo de compilación, de ahí la palabra static
. Este es el reparto que el compilador de C ++ usa internamente también para los lanzamientos implícitos.
reinterpret_cast
s son aplicables en dos escenarios:
- convertir tipos enteros a tipos de puntero y viceversa
- convertir un tipo de puntero a otro. La idea general que tengo es que esto no es portátil y debe evitarse.
Cuando estoy un poco confundido es un uso que necesito, estoy llamando a C ++ desde C y el código C debe retener el objeto C ++, por lo que básicamente contiene un void*
. ¿Qué reparto se debe usar para convertir entre el tipo void *
y el de clase?
He visto el uso de ambos static_cast
y reinterpret_cast
? Aunque, por lo que he estado leyendo, parece que static
es mejor ya que el reparto puede suceder en tiempo de compilación. ¿Aunque dice usar reinterpret_cast
para convertir de un tipo de puntero a otro?
reinterpret_cast
no sucede en tiempo de ejecución. Ambas son declaraciones en tiempo de compilación. De en.cppreference.com/w/cpp/language/reinterpret_cast : "A diferencia de static_cast, pero como const_cast, la expresión reinterpret_cast no se compila con ninguna instrucción de la CPU. Es puramente una directiva del compilador que le indica al compilador que trate la secuencia de bits (representación de objeto) de expresión como si tuviera el tipo new_type ".Respuestas:
El estándar C ++ garantiza lo siguiente:
static_cast
Al utilizar un puntero hacia y desde, sevoid*
conserva la dirección. Es decir, a continuacióna
,b
yc
todos apuntan a la misma dirección:reinterpret_cast
solo garantiza que si lanza un puntero a un tipo diferente y luegoreinterpret_cast
vuelve al tipo original , obtendrá el valor original. Entonces en lo siguiente:a
yc
contienen el mismo valor, pero el valor deb
no está especificado. (en la práctica, generalmente contendrá la misma dirección quea
yc
, pero eso no se especifica en el estándar y puede que no sea cierto en máquinas con sistemas de memoria más complejos).Para lanzar hacia y desde
void*
, sestatic_cast
debe preferir.fuente
b
ya no se especifica en C ++ 11 cuando se usareinterpret_cast
. Y en C ++ 03 se prohibió hacer un elenco deint*
to (aunque los compiladores no implementaron eso y no era práctico, por lo tanto, se cambió para C ++ 11).void*
reinterpret_cast
Un caso cuando
reinterpret_cast
es necesario es cuando se interactúa con tipos de datos opacos. Esto ocurre con frecuencia en las API de proveedores sobre las cuales el programador no tiene control. Aquí hay un ejemplo artificial en el que un proveedor proporciona una API para almacenar y recuperar datos globales arbitrarios:Para usar esta API, el programador debe enviar
VendorGlobalUserData
y volver sus datos .static_cast
no funcionará, uno debe usarreinterpret_cast
:A continuación se muestra una implementación artificial de la API de muestra:
fuente
void*
para eso?USpoofChecker*
, dondeUSpoofChecker
hay una estructura vacía. Sin embargo, debajo del capó, cada vez que pasa unUSpoofChecker*
, se sometereinterpret_cast
a un tipo interno de C ++.La respuesta corta: si no sabe lo que
reinterpret_cast
significa, no lo use. Si lo necesitará en el futuro, lo sabrá.Respuesta completa:
Consideremos los tipos de números básicos.
Cuando convierte, por ejemplo,
int(12)
aunsigned float (12.0f)
su procesador, debe invocar algunos cálculos, ya que ambos números tienen una representación de bits diferente. Esto es lo questatic_cast
significa.Por otro lado, cuando llama a
reinterpret_cast
la CPU no invoca ningún cálculo. Simplemente trata un conjunto de bits en la memoria como si tuviera otro tipo. Entonces, cuando convierteint*
afloat*
esta palabra clave, el nuevo valor (después de desreferenciar el puntero) no tiene nada que ver con el valor anterior en significado matemático.Ejemplo: es cierto que
reinterpret_cast
no es portátil debido a una razón: el orden de bytes (endianness). Pero esta es a menudo sorprendentemente la mejor razón para usarlo. Imaginemos el ejemplo: tiene que leer el número binario de 32 bits del archivo, y sabe que es big endian. Su código debe ser genérico y funciona correctamente en sistemas big endian (por ejemplo, algunos ARM) y little endian (por ejemplo, x86). Por lo tanto, debe verificar el orden de los bytes.Es bien conocido en tiempo de compilación, por lo que puede escribir unapuede escribir una función para lograr esto:constexpr
función:Explicación: la representación binaria de
x
in memory podría ser0000'0000'0000'0001
(big) o0000'0001'0000'0000
(little endian). Después de reinterpretar el byte bajo elp
puntero podría ser respectivamente0000'0000
o0000'0001
. Si usa fundición estática, siempre lo será0000'0001
, sin importar qué endianness se esté utilizando.EDITAR:
En la primera versión hice una función de ejemplo
is_little_endian
para serconstexpr
. Se compila bien en el nuevo gcc (8.3.0) pero el estándar dice que es ilegal. El compilador clang se niega a compilarlo (lo cual es correcto).fuente
short
toma 16 bits en la memoria. CorregidoEl significado de
reinterpret_cast
no está definido por el estándar C ++. Por lo tanto, en teoría areinterpret_cast
podría bloquear su programa. En la práctica, los compiladores intentan hacer lo que esperas, que es interpretar los bits de lo que estás pasando como si fueran del tipo al que estás enviando. Si sabes qué hacen los compiladores que vas a usar,reinterpret_cast
puedes usarlo, pero decir que es portátil sería mentiroso.Para el caso que describe, y prácticamente cualquier caso en el que pueda considerar
reinterpret_cast
, puede usarstatic_cast
u otra alternativa en su lugar. Entre otras cosas, el estándar tiene esto que decir sobre lo que puede esperar destatic_cast
(§5.2.9):Entonces, para su caso de uso, parece bastante claro que el comité de estandarización pretendía que lo usara
static_cast
.fuente
reinterpret_crash
. De ninguna manera un error del compilador me impedirá bloquear mi programa de reinterpretación. ¡Informaré un error lo antes posible! </irony>template<class T, U> T reinterpret_crash(U a) { return *(T*)nullptr; }
Un uso de reinterpret_cast es si desea aplicar operaciones bit a bit a flotantes (IEEE 754). Un ejemplo de esto fue el truco Fast Inverse Square-Root:
https://en.wikipedia.org/wiki/Fast_inverse_square_root#Overview_of_the_code
Trata la representación binaria del flotador como un número entero, lo desplaza a la derecha y lo resta de una constante, reduciendo a la mitad y negando el exponente. Después de volver a convertir a un flotador, se somete a una iteración de Newton-Raphson para que esta aproximación sea más exacta:
Esto se escribió originalmente en C, por lo que utiliza los moldes de C, pero el elenco análogo de C ++ es reinterpret_cast.
fuente
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
pormemcpy
, ¿sigue siendo UB?memcpy
Definitivamente lo haría legal.Puede usar reinterprete_cast para verificar la herencia en tiempo de compilación.
Mire aquí: Uso de reinterpret_cast para verificar la herencia en tiempo de compilación
fuente
Traté de concluir y escribí un elenco seguro simple usando plantillas. Tenga en cuenta que esta solución no garantiza lanzar punteros en una función.
fuente
reinterpret_cast
ya hace en esta situación: "Un puntero de objeto puede convertirse explícitamente en un puntero de objeto de un tipo diferente. [72] Cuando un valorv
de tipo de puntero de objeto se convierte en el tipo de puntero de objeto" puntero a cvT
", el resultado esstatic_cast<cv T*>(static_cast<cv void*>(v))
". - N3797.c++2003
norma que puedo no encontrar quereinterpret_cast
lo hacestatic_cast<cv T*>(static_cast<cv void*>(v))
C++03
lo fueraC++98
. Toneladas de proyectos utilizaron C ++ antiguo en lugar de C. portátil. A veces hay que preocuparse por la portabilidad. Por ejemplo, debe admitir el mismo código en Solaris, AIX, HPUX, Windows. En lo que respecta a la dependencia y portabilidad del compilador es complicado. Entonces, un buen ejemplo de la introducción de un infierno de portabilidad es usar unreinterpret_cast
en su códigoPrimero tienes algunos datos en un tipo específico como int aquí:
Entonces desea acceder a la misma variable que otro tipo como flotante: puede decidir entre
o
BREVE: significa que se usa la misma memoria que un tipo diferente. Por lo tanto, podría convertir representaciones binarias de flotantes como tipo int como arriba en flotantes. 0x80000000 es -0 por ejemplo (la mantisa y el exponente son nulos pero el signo, el msb, es uno. Esto también funciona para dobles y dobles largos.
OPTIMIZE: Creo que reinterpret_cast se optimizaría en muchos compiladores, mientras que el c-casting se realiza mediante pointeraritmética (el valor debe copiarse en la memoria, porque los punteros no pueden apuntar a registros de CPU).
NOTA: ¡En ambos casos, debe guardar el valor emitido en una variable antes de lanzarlo! Esta macro podría ayudar:
fuente
reinterpret_cast
formaint
quefloat&
es un comportamiento indefinido.Una razón para usar
reinterpret_cast
es cuando una clase base no tiene una vtable, pero una clase derivada sí. En ese caso,static_cast
yreinterpret_cast
dará como resultado diferentes valores de puntero (este sería el caso atípico mencionado por jalf arriba ). Solo como descargo de responsabilidad, no estoy afirmando que esto sea parte del estándar, sino la implementación de varios compiladores generalizados.Como ejemplo, tome el siguiente código:
Lo que genera algo como:
En todos los compiladores que probé (MSVC 2015 y 2017, clang 8.0.0, gcc 9.2, icc 19.0.1 - ver godbolt para los últimos 3 ) el resultado del
static_cast
difiere del dereinterpret_cast
2 por 4 (4 para MSVC). El único compilador que advirtió sobre la diferencia fue el sonido metálico, con:Una última advertencia es que si la clase base no tiene miembros de datos (por ejemplo, el
int i;
) entonces clang, gcc e icc devuelven la misma dirección parareinterpret_cast
que parastatic_cast
, mientras que MSVC todavía no.fuente
Aquí hay una variante del programa de Avi Ginsburg que ilustra claramente la propiedad
reinterpret_cast
mencionada por Chris Luengo, flodin y cmdLP: que el compilador trata la ubicación de memoria apuntada como si fuera un objeto del nuevo tipo:Lo que da como resultado una salida como esta:
Se puede ver que el objeto B se construye en la memoria como datos específicos de B primero, seguido por el objeto A incrustado. El
static_cast
devuelve correctamente la dirección del objeto A incrustado, y el puntero creado porstatic_cast
correctamente da el valor del campo de datos. El puntero generado por la ubicación de la memoria dereinterpret_cast
tratab
como si fuera un objeto A simple, por lo que cuando el puntero intenta obtener el campo de datos, devuelve algunos datos específicos de B como si fueran los contenidos de este campo.Un uso de
reinterpret_cast
es convertir un puntero a un entero sin signo (cuando los punteros y los enteros sin signo son del mismo tamaño):int i;
unsigned int u = reinterpret_cast<unsigned int>(&i);
fuente
Respuesta rápida: use
static_cast
si se compila, de lo contrario recurra areinterpret_cast
.fuente
¡Lea las preguntas frecuentes ! Mantener datos de C ++ en C puede ser arriesgado.
En C ++, un puntero a un objeto se puede convertir
void *
sin ninguna conversión . Pero no es cierto al revés. Necesitaría unstatic_cast
para recuperar el puntero original.fuente