c++ casting reinterpret-cast static-cast

¿Por qué tenemos reinterpret_cast en C++ cuando dos static_cast encadenado pueden hacer su trabajo?



casting reinterpret-cast (7)

Digamos que quiero lanzar A* to char* y viceversa, tenemos dos opciones (es 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 static_cast encadenado pueden hacer su trabajo?

Algunos de ustedes podrían pensar que este tema es un duplicado de los temas anteriores, como se indica en la parte inferior de esta publicación, pero no lo es. Esos temas solo discuten teóricamente, pero ninguno de ellos ofrece ni un solo ejemplo que demuestre por reintepret_cast realmente se necesita static_cast , y dos static_cast seguramente fallarían. Estoy de acuerdo, un static_cast fallaría. Pero ¿qué hay de dos?

Si la sintaxis de static_cast encadenado parece engorrosa, podemos escribir una plantilla de función para que sea más fácil 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:

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

Además, vea esta situación en la que any_cast puede ser útil: conversión adecuada para funciones de miembro de lectura y escritura fstream .

Así que mi pregunta es básicamente,

  • ¿Por qué tenemos reinterpret_cast en C ++?
  • Por favor, muéstrame incluso un solo ejemplo en el que dos static_cast encadenados seguramente no hagan el mismo trabajo.

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

static_cast está diciendo que convierta los datos del tipo X en Y. reinterpret_cast está diciendo que interprete los datos en X como una Y.

Bien puede ser que las operaciones subyacentes sean las mismas, y que cualquiera de las dos funcionaría en muchos casos. Pero hay una diferencia conceptual entre decir, por favor, convierta X en una Y, y decir "sí, sé que estos datos se declaran como una X, pero úselos como si realmente fueran una Y".


El uso de fundición estilo C no es más seguro. Nunca se pueden mezclar diferentes tipos de cheques. Los lanzamientos de C ++ le ayudan a asegurarse de que los lanzamientos de tipo se realicen según los objetos relacionados (según el lanzamiento que use). Esta es la forma más recomendada de usar los moldes que con los moldes tradicionales de estilo C que siempre son dañinos.


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

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

  • Un valor de tipo integral o tipo de 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 valor de tipo "puntero a miembro de X de tipo T1 " se puede convertir explícitamente a un valor 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.

Además, desde C ++ 03 9.2 / 17:

  • Un puntero a un objeto POD-struct, convertido adecuadamente usando un reinterpret_cast , apunta a su miembro inicial (o si ese miembro es un campo de bits, luego a la unidad en la que reside) y viceversa.

Mire, gente, realmente no necesita reinterpret_cast, static_cast, o incluso los otros dos modelos de C ++ (dynamic * y const).

Usar un modelo de estilo C es más corto y le permite hacer todo lo que le permiten los cuatro tipos de estilo C ++.

anyType someVar = (anyOtherType)otherVar;

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

* Está bien, es posible que necesite dinámica


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

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

es posible que desee tener dicho código para llegar a un puerto de entrada-salida del dispositivo asignado en memoria.


Por lo que puedo decir, su elección 1 (dos static_cast encadenado) es un comportamiento indefinido temido. La conversión estática solo garantiza que el puntero de lanzamiento se anule * y luego el puntero original funcione de manera tal que el puntero resultante de estas conversiones aún apunte al objeto original. Todas las otras conversiones son UB. Para los punteros a objetos (instancias de las clases definidas por el usuario) static_cast puede alterar el valor del puntero.

Para reinterpret_cast, solo altera el tipo de puntero y, por lo que sé, nunca toca el valor del puntero.

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

EDITAR: 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 C ++ 03, el borrador que considero actual es n3225.pdf). Describe todas las conversiones permitidas, y supongo que cualquier cosa que no esté listada específicamente = UB. Por lo que puede hacer estallar tu PC si así lo desea.


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''; }