Namespaces
Variants

reinterpret_cast conversion

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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).

1) Una expresión de tipo integral, enumeración, puntero o puntero-a-miembro puede convertirse a su propio tipo. El valor resultante es el mismo que el valor de la expresión .
2) Un puntero puede convertirse a cualquier tipo integral lo suficientemente grande para contener todos los valores de su tipo (por ejemplo, a std::uintptr_t ).
3) Un valor de cualquier tipo integral o de enumeración puede convertirse a un tipo puntero. Un puntero convertido a un entero de tamaño suficiente y de vuelta al mismo tipo puntero garantiza tener su valor original; de lo contrario, el puntero resultante no puede ser desreferenciado de forma segura (la conversión de ida y vuelta en la dirección opuesta no está garantizada; el mismo puntero puede tener múltiples representaciones enteras). La constante de puntero nulo NULL o el entero cero no garantizan producir el valor de puntero nulo del tipo destino; 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)
5) Cualquier tipo de puntero a objeto 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 .
6) Una lvalue (until C++11) glvalue (since C++11) expresión de 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 .
7) Cualquier puntero a función puede convertirse a un puntero a un tipo de función diferente. El resultado no está especificado, pero convertir dicho puntero de vuelta a puntero al tipo de función original produce el puntero a la función original. El puntero resultante solo puede llamarse de forma segura si su tipo de función es call-compatible con el tipo de función original.
8) En algunas implementaciones (en particular, en cualquier sistema compatible con POSIX según lo requerido por 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.
9) El valor de puntero nulo de cualquier tipo de puntero puede convertirse a cualquier otro tipo de puntero, resultando en el valor de puntero nulo de ese tipo. Nótese que la constante de puntero nulo nullptr o cualquier otro valor de tipo std::nullptr_t no puede convertirse a un puntero con reinterpret_cast : para este propósito debe utilizarse conversión implícita o static_cast .
10) Un puntero a función miembro puede convertirse a un puntero a una función miembro diferente de un tipo distinto. La conversión de vuelta al tipo original produce el valor original, de lo contrario el puntero resultante no puede utilizarse de forma segura.
11) Un puntero a objeto miembro de alguna clase 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) ;
  • un xvalue si target-type es una referencia a rvalue a tipo objeto;
(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_call es el mismo tipo que T_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

reinterpret_cast

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)