reinterpret_cast
conversion
Convierte entre tipos reinterpretando el patrón de bits subyacente.
Contenidos |
Sintaxis
reinterpret_cast<
tipo-destino
>(
expresión
)
|
|||||||||
Devuelve un valor de tipo target-type .
Explicación
A diferencia de static_cast , pero al igual que const_cast , la expresión reinterpret_cast no se compila a ninguna instrucción de CPU (excepto cuando se convierte entre enteros y punteros, o entre punteros en arquitecturas poco comunes donde la representación de punteros depende de su tipo). Es principalmente una directiva en tiempo de compilación que instruye al compilador a tratar expression como si tuviera el tipo target-type .
Solo las siguientes conversiones pueden realizarse con reinterpret_cast , excepto cuando dichas conversiones eliminen la constancia (o volatilidad).
static_cast
o
conversión implícita
deberían usarse para este propósito.
|
4)
Cualquier valor de tipo
std::nullptr_t
, incluyendo
nullptr
puede convertirse a cualquier tipo integral como si fuera
(
void
*
)
0
, pero ningún valor, ni siquiera
nullptr
puede convertirse a
std::nullptr_t
: debe usarse
static_cast
para ese propósito.
|
(desde C++11) |
T1*
puede ser convertido a otro tipo de puntero a objeto
cv
T2*
. Esto es exactamente equivalente a
static_cast
<
cv
T2
*
>
(
static_cast
<
cv
void
*
>
(
expression
)
)
(lo que implica que si el requisito de alineación de
T2
no es más estricto que el de
T1
, el valor del puntero no cambia y la conversión del puntero resultante de vuelta a su tipo original produce el valor original). En cualquier caso, el puntero resultante solo puede ser desreferenciado de forma segura si el valor desreferenciado es
accesible por tipo
.
T1
puede convertirse a referencia a otro tipo
T2
. El resultado es el de
*
reinterpret_cast
<
T2
*
>
(
p
)
, donde
p
es un puntero de tipo "puntero a
T1
" al objeto o función designado por
expresión
. No se
materializa o crea
(since C++17)
ningún temporal, no se realiza ninguna copia, no se llaman constructores ni funciones de conversión. La referencia resultante solo puede accederse de forma segura si es
type-accessible
.
dlsym
), un puntero a función puede convertirse a
void
*
o a cualquier otro puntero a objeto, o viceversa. Si la implementación admite la conversión en ambas direcciones, la conversión al tipo original produce el valor original; de lo contrario, el puntero resultante no puede ser desreferenciado o invocado de forma segura.
T1
puede convertirse a un puntero a otro objeto miembro de otra clase
T2
. Si la alineación de
T2
no es más estricta que la de
T1
, la conversión de vuelta al tipo original
T1
produce el valor original; de lo contrario, el puntero resultante no puede utilizarse de forma segura.
Como con todas las expresiones de conversión, el resultado es:
- un lvalue si target-type es un tipo de referencia lvalue o una referencia rvalue a tipo función (desde C++11) ;
|
(since C++11) |
- un prvalue en caso contrario.
Alias de tipos
Accesibilidad de tipos
Si un tipo
T_ref
es
similar
a cualquiera de los siguientes tipos, un objeto de
tipo dinámico
T_obj
es
accesible por tipo
a través de un
lvalue
(hasta C++11)
glvalue
(desde C++11)
de tipo
T_ref
:
- char , unsigned char o std::byte (desde C++17) : esto permite examinar la representación del objeto de cualquier objeto como un arreglo de bytes.
-
T_obj -
el tipo con signo o sin signo correspondiente a
T_obj
Si un programa intenta leer o modificar el valor almacenado de un objeto a través de un lvalue (until C++11) glvalue (since C++11) a través del cual no es accesible por tipo, el comportamiento es indefinido.
Esta regla habilita el análisis de alias basado en tipos, en el cual un compilador asume que el valor leído a través de un glvalue de un tipo no es modificado por una escritura a un glvalue de un tipo diferente (sujeto a las excepciones mencionadas anteriormente).
Tenga en cuenta que muchos compiladores de C++ relajan esta regla, como una extensión de lenguaje no estándar, para permitir el acceso de tipo incorrecto a través del miembro inactivo de una union (dicho acceso no es indefinido en C).
Compatibilidad de llamadas
Si se satisface cualquiera de las siguientes condiciones, un tipo
T_call
es
call-compatible
con un tipo de función
T_func
:
-
T_calles el mismo tipo queT_func.
|
(desde C++17) |
Si una función es llamada a través de una expresión cuyo tipo de función no es compatible para llamada con el tipo de la definición de la función llamada, el comportamiento es indefinido.
Notas
Suponiendo que se cumplan los requisitos de alineación, un reinterpret_cast no cambia el valor de un puntero excepto en algunos casos limitados relacionados con objetos pointer-interconvertible :
struct S1 { int a; } s1; struct S2 { int a; private: int b; } s2; // no es standard-layout union U { int a; double b; } u = {0}; int arr[2]; int* p1 = reinterpret_cast<int*>(&s1); // el valor de p1 es "puntero a s1.a" porque // s1.a y s1 son pointer-interconvertible int* p2 = reinterpret_cast<int*>(&s2); // el valor de p2 no cambia por reinterpret_cast // y es "puntero a s2". int* p3 = reinterpret_cast<int*>(&u); // el valor de p3 es "puntero a u.a": // u.a y u son pointer-interconvertible double* p4 = reinterpret_cast<double*>(p3); // el valor de p4 es "puntero a u.b": u.a y // u.b son pointer-interconvertible porque // ambos son pointer-interconvertible con u int* p5 = reinterpret_cast<int*>(&arr); // el valor de p5 no cambia por reinterpret_cast // y es "puntero a arr"
Realizar un acceso a miembro de clase que designa un miembro de datos no estático o una función miembro no estática sobre un glvalue que no designa realmente un objeto del tipo apropiado - como uno obtenido a través de un reinterpret_cast - resulta en comportamiento indefinido:
struct S { int x; }; struct T { int x; int f(); }; struct S1 : S {}; // diseño estándar struct ST : S, T {}; // no es de diseño estándar S s = {}; auto p = reinterpret_cast<T*>(&s); // el valor de p es "puntero a s" auto i = p->x; // la expresión de acceso a miembro de clase es comportamiento indefinido; // s no es un objeto T p->x = 1; // comportamiento indefinido p->f(); // comportamiento indefinido S1 s1 = {}; auto p1 = reinterpret_cast<S*>(&s1); // el valor de p1 es "puntero al subobjeto S de s1" auto i = p1->x; // OK p1->x = 1; // OK ST st = {}; auto p2 = reinterpret_cast<S*>(&st); // el valor de p2 es "puntero a st" auto i = p2->x; // comportamiento indefinido p2->x = 1; // comportamiento indefinido
Muchos compiladores emiten advertencias de "strict aliasing" en tales casos, aunque técnicamente tales construcciones infringen algo más que el párrafo comúnmente conocido como la "strict aliasing rule".
El propósito del aliasing estricto y las reglas relacionadas es permitir el análisis de alias basado en tipos, el cual sería diezmado si un programa pudiera crear válidamente una situación donde dos punteros a tipos no relacionados (por ejemplo, un int * y un float * ) pudieran existir simultáneamente y ambos pudieran usarse para cargar o almacenar la misma memoria (consulte este correo en el reflector SG12 ). Por lo tanto, cualquier técnica que aparentemente sea capaz de crear tal situación necesariamente invoca comportamiento indefinido.
Cuando es necesario interpretar los bytes de un objeto como un valor de un tipo diferente, std::memcpy o std::bit_cast (desde C++20) pueden utilizarse:
double d = 0.1; std::int64_t n; static_assert(sizeof n == sizeof d); // n = *reinterpret_cast<std::int64_t*>(&d); // Comportamiento indefinido std::memcpy(&n, &d, sizeof d); // Correcto n = std::bit_cast<std::int64_t>(d); // también Correcto
|
Si la implementación proporciona std::intptr_t y/o std::uintptr_t , entonces una conversión de un puntero a un tipo objeto o cv void a estos tipos siempre está bien definida. Sin embargo, esto no está garantizado para un puntero a función. |
(since C++11) |
En C, la copia y asignación de agregados acceden al objeto agregado como un todo. Pero en C++ tales acciones siempre se realizan mediante una llamada a función miembro, que accede a los subobjetos individuales en lugar del objeto completo (o, en el caso de uniones, copia la representación del objeto, es decir, mediante unsigned char ).
Palabras clave
Ejemplo
Demuestra algunos usos de reinterpret_cast :
#include <cassert> #include <cstdint> #include <iostream> int f() { return 42; } int main() { int i = 7; // pointer to integer and back std::uintptr_t v1 = reinterpret_cast<std::uintptr_t>(&i); // static_cast is an error std::cout << "The value of &i is " << std::showbase << std::hex << v1 << '\n'; int* p1 = reinterpret_cast<int*>(v1); assert(p1 == &i); // pointer to function to another and back void(*fp1)() = reinterpret_cast<void(*)()>(f); // fp1(); undefined behavior int(*fp2)() = reinterpret_cast<int(*)()>(fp1); std::cout << std::dec << fp2() << '\n'; // safe // type aliasing through pointer char* p2 = reinterpret_cast<char*>(&i); std::cout << (p2[0] == '\x7' ? "This system is little-endian\n" : "This system is big-endian\n"); // type aliasing through reference reinterpret_cast<unsigned int&>(i) = 42; std::cout << i << '\n'; [[maybe_unused]] const int &const_iref = i; // int &iref = reinterpret_cast<int&>( // const_iref); // compiler error - can't get rid of const // Must use const_cast instead: int &iref = const_cast<int&>(const_iref); }
Salida posible:
The value of &i is 0x7fff352c3580 42 This system is little-endian 42
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.
| DR | Aplicado a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 195 | C++98 |
conversión entre punteros a función
y punteros a objeto no permitida |
hecho condicionalmente soportado |
| CWG 658 | C++98 |
el resultado de conversiones de punteros no estaba especificado
(excepto para conversiones de vuelta al tipo original) |
se proporcionó especificación para punteros
cuyos tipos apuntados satisfacen los requisitos de alineación |
| CWG 799 | C++98 |
no estaba claro qué conversión de identidad
puede realizar reinterpret_cast |
aclarado |
| CWG 1268 | C++11 |
reinterpret_cast
solo podía convertir
lvalues a tipos referencia |
xvalues también permitidos |
| CWG 2780 | C++98 |
reinterpret_cast
no podía convertir
lvalues de función a otros tipos referencia |
permitido |
| CWG 2939 | C++17 |
reinterpret_cast
podía convertir
prvalues a tipos referencia a rvalue |
no permitido |
Referencias
- Estándar C++23 (ISO/IEC 14882:2024):
-
- 7.6.1.10 Conversión reinterpretada [expr.reinterpret.cast]
- Estándar C++20 (ISO/IEC 14882:2020):
-
- 7.6.1.9 Conversión de reinterpretación [expr.reinterpret.cast]
- Estándar C++17 (ISO/IEC 14882:2017):
-
- 8.2.10 Conversión de reinterpretación [expr.reinterpret.cast]
- Estándar C++14 (ISO/IEC 14882:2014):
-
- 5.2.10 Conversión reinterpretada [expr.reinterpret.cast]
- Estándar C++11 (ISO/IEC 14882:2011):
-
- 5.2.10 Conversión reinterpretada [expr.reinterpret.cast]
- Estándar C++98 (ISO/IEC 14882:1998):
-
- 5.2.10 Conversión reinterpretada [expr.reinterpret.cast]
- Estándar C++03 (ISO/IEC 14882:2003):
-
- 5.2.10 Reinterpret cast [expr.reinterpret.cast]
Véase también
const_cast
conversión
|
añade o elimina const |
static_cast
conversión
|
realiza conversiones básicas |
dynamic_cast
conversión
|
realiza conversiones polimórficas verificadas |
| conversiones explícitas | conversiones permisivas entre tipos |
| conversiones estándar | conversiones implícitas de un tipo a otro |
|
(C++20)
|
reinterpreta la representación del objeto de un tipo como la de otro
(plantilla de función) |