c++ pointers casting reinterpret-cast type-punning

c++ - ¿Es reinterpret_cast en su mayoría inútil?



pointers casting (5)

Esencialmente, a lo que se reduce es que el resultado de una operación de reinterpret_cast puntero a puntero no se puede usar de forma segura para otra cosa que no sea volver al tipo de puntero original.

Tienes razón, el estándar se rompió, pero N3242 intenta solucionarlo:

Definición del valor de reinterpret_cast<T*>

N3242, [expr.reinterpret.cast]:

Cuando un prvalue v de tipo "puntero a T1" se convierte al tipo "puntero a cv T2", el resultado es static_cast<cv T2*>(static_cast<cv void*>(v)) si T1 y T2 son estándar -Los tipos de asignación (3.9) y los requisitos de alineación de T2 no son más estrictos que los de T1.

Esto todavía no define nada; Por diversión, el texto menos irrelevante sobre static_cast :

Un prvalor de tipo "puntero a cv1 void" se puede convertir a un prvalor de tipo "puntero a cv2 T", donde T es un tipo de objeto y cv2 es la misma calificación cv que la cv1 o más calificada que cv1. El valor del puntero nulo se convierte al valor del puntero nulo del tipo de destino. Un valor de tipo puntero a objeto convertido a "puntero a cv void" y viceversa, posiblemente con una calificación cv diferente, tendrá su valor original.

"Convenientemente convertido"

N3242, [class.mem]:

Un puntero a un objeto de estructura de diseño estándar, 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.

¿Qué tipo de estilo es ese? " adecuadamente "? jajaja

Claramente, estos chicos no saben cómo escribir una especificación.

¿El estilo C lanza la única opción segura aquí?

No ayuda

La norma está rota, rota, rota.

Pero todos entienden lo que realmente significa. El subtexto de la norma dice:

Cuando un prvalue v de tipo "puntero a T1" se convierte al tipo "puntero a cv T2", el resultado is static_cast<cv T2*>(static_cast<cv void*>(v)) apunta a la dirección de la memoria como v si T1 y T2 son tipos de diseño estándar (3.9) y los requisitos de alineación de T2 no son más estrictos que los de T1.

Eso ciertamente no es perfecto (pero no es peor que muchas otras partes de la norma), pero en 2 minutos escribí una especificación mejor que la que hizo el comité en una década.

He leído varias preguntas anteriores sobre el uso de reinterpret_cast , y también he leído la redacción relevante en el estándar de C ++. Esencialmente, a lo que se reduce es que el resultado de una operación de reinterpret_cast puntero a puntero no se puede usar de forma segura para otra cosa que no sea volver al tipo de puntero original.

Sin embargo, en la práctica, la mayoría de los usos de reinterpret_cast mundo real parecen estar basados ​​en la suposición (errónea) de que reinterpret_cast es lo mismo que un reparto de estilo C. Por ejemplo, he visto muchos códigos que usan reinterpret_cast para convertir de char* a unsigned char* con el propósito de las rutinas de conversión de conjuntos de caracteres. Esto es completamente inofensivo, pero estrictamente hablando, no es portátil: no hay garantía de que un reinterpret_cast de char* a unsigned char* no bloquee su programa cuando intente eliminar la referencia del unsigned char* charign unsigned char* .

Parece que el único otro uso real de reinterpret_cast que tiene garantías reales, de acuerdo con el estándar, es la conversión de puntero a entero, y viceversa.

Y, sin embargo, hay muchos casos en los que quisiéramos (y deberíamos poder) convertir de forma segura entre diferentes tipos de punteros. Por ejemplo: uint16_t* para el nuevo C ++ 0x char16_t* , o realmente cualquier puntero a un tipo de datos básico que tenga el mismo tamaño / alineación que el tipo original. Sin embargo, reinterpret_cast no ofrece garantías de que esto funcione.

Pregunta: ¿Cómo podemos convertir de forma segura los punteros a tipos de datos básicos del mismo tamaño / alineación, como char* -> unsigned char* ? Ya que reinterpret_cast no parece garantizar que esto realmente funcione, ¿son los lanzamientos de estilo C la única opción segura aquí?


el resultado de una operación reinterpret_cast de puntero a puntero no se puede usar de forma segura para otra cosa que no sea volver al tipo de puntero original.

Eso no suena bien. Suponiendo sizeof(void *) > sizeof(int *) , void *foo; reinterpret_cast<void *>(reinterpret_cast<int *>(foo)) void *foo; reinterpret_cast<void *>(reinterpret_cast<int *>(foo)) podría dejarlo con un valor de puntero truncado.

¿No es que reinterpret_cast es, en la práctica, simplemente el equivalente a la conversión por defecto de C?


no hay garantía de que un reinterpret_cast de char * a unsigned char * no bloquee el programa cuando intente eliminar la referencia al puntero unsigned char *.

No puedes hacer ese reparto de ninguna otra manera, por lo que debes confiar en lo que hace tu compilador con este reparto completamente razonable.

Ya que reinterpret_cast no parece garantizar que esto realmente funcione, ¿son los lanzamientos de estilo C la única opción segura aquí?

El reparto de estilo C solo se asignará a reinterpret_cast por lo que será exactamente el mismo. En algún momento tienes que confiar en tu compilador. El Estándar tiene un límite en el que simplemente dice "no. Lea el manual de su compilador". Cuando se trata de punteros cruzados, este es un punto así. Le permite leer un char utilizando un valor de unsigned char . Un compilador que no puede lanzar un char* a un unsigned char* utilizable para hacer tal cosa es casi inutilizable y no existe por esa razón.


El estándar especifica lo que tiene que suceder en todas las plataformas, no tiene que hacer eso. Si limita sus requisitos de portabilidad a plataformas donde su reinterpret_cast realmente funciona, está bien.

En plataformas que realmente soportan un uint16_t, es probable que el reparto funcione. En plataformas donde char16_t tiene 18, 24, 32 o 36 bits de ancho, puede que no haga lo correcto. La pregunta es, ¿tienes que soportar tales plataformas? El estándar de idioma quiere.


Hay otras garantías en otras partes de la norma (vea la sección sobre representación de tipo que, IIRC, exige que los tipos correspondientes sin signo y firmados compartan la representación de los valores comunes, aún así, IIRC, también hay algún texto que garantiza que puede leer cualquier cosa como caracteres ). Pero tenga en cuenta también que hay algunos lugares que reducen incluso la sección que está leyendo (que establece que las cosas están definidas por la implementación y no especificadas): algunas formas de tipificación de caracteres son comportamientos indefinidos.