Forma adecuada de tipos de puntero de fundición


Considerando el siguiente código (y el hecho de que VirtualAlloc() devuelve a void*):

BYTE* pbNext = reinterpret_cast<BYTE*>(
    VirtualAlloc(NULL, cbAlloc, MEM_COMMIT, PAGE_READWRITE));

¿Por qué se elige reinterpret_cast en lugar de static_cast?

Solía pensar que reinterpret_cast está bien para, por ejemplo, lanzar punteros hacia y desde tipos enteros (como, por ejemplo, DWORD_PTR), pero para lanzar desde un void* a un BYTE*, no está bien static_cast?

¿Hay alguna (sutil?) las diferencias en este caso en particular, o son solo los dos moldes de puntero válidos?

¿El estándar C++ tiene un preferencia por este caso, sugiriendo una forma en lugar de la otra?

Author: Mr.C64, 2013-03-23

3 answers

Para punteros a tipos fundamentales ambos moldes tienen el mismo significado; por lo que está en lo correcto que static_cast está bien.

Al convertir entre algunos tipos de puntero, es posible que la dirección de memoria específica mantenida en el puntero necesite cambiar.

Ahí es donde los dos moldes difieren. static_cast hará el ajuste apropiado. reinterpret_cast no lo hará.

Por esa razón, es una buena regla general para static_cast entre tipos de puntero a menos que sepa que reinterpret_cast se desea.

 33
Author: Drew Dormann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-30 15:21:44

Deberías static_cast. Use static_cast en los casos en que esté deshaciendo una conversión implícita.

En este caso particular, sin embargo, no hay diferencia porque estás convirtiendo desde void*. Pero en general, reinterpret_casting entre dos punteros de objeto se define como (§5.2.10/7):

Un puntero de objeto se puede convertir explícitamente en un puntero de objeto de un tipo diferente. Cuando un prvalue v de tipo "puntero a T1" se convierte en el tipo " puntero a cv T2", el resultado es static_cast<cv T2*>(static_cast<cv void*>(v)) si tanto T1 como T2 son tipos de disposición estándar y los requisitos de alineación de T2 no son más estrictos que los de T1, o si cualquiera de los tipos es void. Convertir un prvalue de tipo " puntero a T1" al tipo "puntero a T2" (donde T1 y T2 son tipos de objeto y donde los requisitos de alineación de T2 no son más estrictos que los de T1) y volver a su tipo original produce el valor del puntero original. El resultado de cualquier otro puntero la conversión no está especificada.

Énfasis mío. Puesto que T1 para ti ya es void*, el reparto a void* en reinterpret_cast no hace nada. Esto no es cierto en general, que es lo que Drew Dormann está diciendo :

#include <iostream>

template <typename T>
void print_pointer(const volatile T* ptr)
{
    // this is needed by oversight in the standard
    std::cout << static_cast<void*>(const_cast<T*>(ptr)) << std::endl;
}

struct base_a {};
struct base_b {};
struct derived : base_a, base_b {};

int main()
{
    derived d;

    base_b* b = &d; // implicit cast

    // undo implicit cast with static_cast
    derived* x = static_cast<derived*>(b);

    // reinterpret the value with reinterpret_cast
    derived* y = reinterpret_cast<derived*>(b);

    print_pointer(&d);
    print_pointer(x);
    print_pointer(y);
}

Salida:

00CBFD5B
00CBFD5B
00CBFD5C

(Tenga en cuenta que debido a que y no apunta realmente a un derived, usarlo es un comportamiento indefinido.)

Aquí, reinterpret_cast viene con un valor diferente porque pasa por void*. Es por eso que debes usar static_cast cuando puedas, y reinterpret_cast cuando tengas que hacerlo.

 20
Author: GManNickG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:04

Usar static_cast para lanzar un puntero hacia y desde void* está garantizado para preservar la dirección.

reinterpret_cast por otro lado, garantiza que si envía el puntero de un tipo a otro, y de vuelta al tipo original, la dirección se conserva.

Aunque con la mayoría de las implementaciones, verá los mismos resultados al usar cualquiera de estas, static_cast debe ser preferido.

Y con C++11 recuerdo que usar reinterpret_cast para void* tiene un comportamiento bien definido. Antes de que esto el comportamiento estaba prohibido.

It is not permitted to use reinterpret_cast to convert between pointers to object type and pointers to void.

Propuesta de resolución (agosto de 2010):

Change 5.2.10 [expr.reinterpretar.cast] paragraph 7 as follows:

Un puntero de objeto se puede convertir explícitamente en un puntero de objeto de un tipo diferente. Cuando un prvalue v de tipo "puntero a T1" es convertido al tipo "puntero a cv T2", el resultado es static_cast(static_cast (v)) si tanto T1 como T2 son de diseño estándar tipos (3.9 [básico.tipos]) y la alineación los requisitos de T2 no son más estrictas que las de T1, o si cualquiera de los tipos es nulo.

Convirtiendo un prvalue del tipo "puntero a T1" al tipo " puntero a T2 " (donde T1 y T2 son tipos de objetos y donde la alineación requisitos de T2 no son más estrictos que los de T1) y volver a su el tipo original produce el valor del puntero original. El resultado de cualquier otra conversión de puntero no está especificada.

Más información aquí.

Gracias a Jesse Bueno para el enlace.

 7
Author: Tuxdude,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:17:55