¿Por qué reinterpretar cast en C++ cuando dos cast estáticos encadenados pueden hacer su trabajo?


Digamos que quiero lanzar A* a char* y viceversa, tenemos dos opciones (quiero decir, muchos de nosotros pensamos que tenemos dos opciones, porque ambas parecen funcionar! De ahí la confusión!):

struct A
{
    int age;
    char name[128];
};

A a;
char *buffer = static_cast<char*>(static_cast<void*>(&a)); //choice 1
char *buffer = reinterpret_cast<char*>(&a); //choice 2

Ambos funcionan bien.

//convert back
A *pA = static_cast<A*>(static_cast<void*>(buffer)); //choice 1
A *pA = reinterpret_cast<A*>(buffer); //choice 2

¡Incluso esto funciona bien!

Entonces, ¿por qué tenemos reinterpret_cast en C++ cuando dos encadenados static_cast puede hacer su trabajo?

Algunos de ustedes podrían pensar que este tema es un duplicado de los temas anteriores, como los enumerados en la parte inferior de este post, pero es ni. Los temas a discutir sólo teóricamente, pero ninguno de ellos da incluso un solo ejemplo que demuestra ¿por qué reintepret_cast es realmente necesario, y dos static_cast sería seguramente error. Estoy de acuerdo, un static_cast fallaría. ¿Pero qué tal dos?

Si la sintaxis de two chained static_cast parece engorrosa, entonces podemos escribir una plantilla de función para hacerla más amigable para el programador:

template<class To, class From>
To any_cast(From v)
{
    return static_cast<To>(static_cast<void*>(v));
}

Y luego podemos usar esto, como: {[17]]}

char *buffer = any_cast<char*>(&a); //choice 1
char *buffer = reinterpret_cast<char*>(&a); //choice 2

//convert back
A *pA = any_cast<A*>(buffer); //choice 1
A *pA = reinterpret_cast<A*>(buffer); //choice 2

También ver esta situación donde any_cast puede ser útil: Casting apropiado para las funciones miembro de lectura y escritura de fstream.

Así que mi pregunta básicamente es, {[17]]}

  • ¿Por qué tenemos reinterpret_cast en C++?
  • Por favor, muéstrame un solo ejemplo donde dos encadenados static_cast seguramente no haría el mismo trabajo?

Author: Community, 2011-02-17

7 answers

Hay cosas que reinterpret_cast puede hacer que ninguna secuencia de static_cast s puede hacer (todo desde C++03 5.2.10):

  • Un puntero se puede convertir explícitamente a cualquier tipo integral lo suficientemente grande como para mantenerlo.

  • Un valor de tipo integral o tipo enumeración se puede convertir explícitamente en un puntero.

  • Un puntero a una función se puede convertir explícitamente en un puntero a una función de un tipo diferente.

  • Un rvalue de tipo " puntero a miembro de X de tipo T1" se puede convertir explícitamente en un r-value de tipo "puntero a miembro de Y de tipo T2" si T1 y T2 son ambos tipos de función o ambos tipos de objeto.

También, desde C++03 9.2 / 17:

  • Un puntero a un objeto POD-struct, convenientemente convertido usando un reinterpret_cast, apunta a su miembro inicial (o si ese miembro es un campo de bits, entonces a la unidad en la que reside) y viceversa.
 36
Author: James McNellis,
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
2011-02-17 07:23:52

Necesita reinterpret_cast para obtener un puntero con una dirección codificada (como aquí):

int* pointer = reinterpret_cast<int*>( 0x1234 );

Es posible que desee tener dicho código para llegar a algún puerto de entrada-salida de dispositivo mapeado en memoria.

 14
Author: sharptooth,
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 11:54:31

Un ejemplo concreto:

char a[4] = "Hi\n";
char* p = &a;

f(reinterpret_cast<char (&)[4]>(p));  // call f after restoring full type
      // ^-- any_cast<> can't do this...

// e.g. given...
template <typename T, int N>   // <=--- can match this function
void f(T (&)[N]) { std::cout << "array size " << N << '\n'; }
 6
Author: Tony Delroy,
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
2011-02-17 08:07:37

Aparte de las razones prácticas que otros han dado donde hay una diferencia en lo que pueden hacer, es bueno tener porque está haciendo un trabajo diferente.

Static_cast está diciendo que por favor convierta datos de tipo X a Y. reinterpret_cast está diciendo por favor interprete los datos en X como una Y.

Bien puede ser que las operaciones subyacentes sean las mismas, y eso funcionaría en muchos casos. Pero hay una diferencia conceptual entre decir por favor convierte X en a Y, y diciendo "sí, sé que estos datos se declaran como una X, pero por favor utilícelos como si realmente fuera una Y".

 4
Author: jcoder,
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
2011-02-17 08:29:35

Por lo que puedo decir su elección 1 (dos static_cast encadenado) es temido comportamiento indefinido. La conversión estática solo garantiza que la conversión del puntero a void * y luego de vuelta al puntero original funcione de manera que el puntero resultante de estas conversiones siga apuntando al objeto original. Todas las demás conversiones son UB. Para punteros a objetos (instancias de las clases definidas por el usuario) static_cast puede alterar el valor del puntero.

Para el reinterpret_cast - solo altera el tipo de el puntero y que yo sepa - nunca toca el valor del puntero.

Así que técnicamente hablando las dos opciones no son equivalentes.

EDIT: Para la referencia, static_cast se describe en la sección 5.2.9 del borrador actual de C++0x (lo siento, no tengo el estándar de C++03, el borrador que considero actual es n3225.pdf). Describe todas las conversiones permitidas, y supongo que cualquier cosa que no esté específicamente listada = UB. Así que puede soplar PC si decide hacerlo.

 3
Author: Tomek,
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
2011-02-17 10:06:09

Miren, gente, realmente no necesitan reinterpret_cast, static_cast, o incluso los otros dos estilos de C++ (dynamic* y const).

El uso de un cast de estilo C es más corto y le permite hacer todo lo que los cuatro cast de estilo C++le permiten hacer.

anyType someVar = (anyOtherType)otherVar;

Entonces, ¿por qué usar los casts de estilo C++? Legibilidad. En segundo lugar, porque los moldes más restrictivos permiten una mayor seguridad del código.

* bueno, es posible que necesite dinámica

 0
Author: Alexander Rafferty,
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
2011-02-17 07:16:55

El uso de C Style casting no es más seguro. Nunca comprueba si diferentes tipos pueden mezclarse entre sí. C++ casts le ayuda a asegurarse de que los casts de tipo se realizan según los objetos relacionados (en función del cast que utilice). Esta es la forma más recomendada de usar moldes que usar los moldes tradicionales de estilo C que siempre son dañinos.

 0
Author: Edwin,
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
2014-10-11 06:59:31