2012-04-14 19 views
31

Aparentemente, el compilador los considera tipos no relacionados y, por lo tanto, se requiere reinterpret_cast. ¿Por qué es esta la regla?¿Por qué no puedo static_cast entre char * y unsigned char *?

+0

Estoy tomando el hash SHA-1 de una cadena. 'c_str()' devuelve 'const char *' y la función SHA-1 toma 'const unsigned char *' como argumento. – Nick

+0

¿Y qué espera que suceda si esa cadena contiene valores de caracteres negativos? – Pubby

+0

Espero que cualquier valor negativo 'c' se convierta en' c + 256', como es estándar en la conversión de un byte firmado a uno sin signo. Honestamente, solo estoy haciendo la conversión para calcular un valor hash. No me importa cómo se conviertan, siempre y cuando se conviertan de la misma manera todas las veces. – Nick

Respuesta

26

son completamente diferentes tipos véase la norma:

3.9.1 tipos fundamentales [basic.fundamental]

1 objetos declarados como caracteres char) deberán ser suficientemente grandes para tienda de cualquier miembro de la conjunto de caracteres básicos de la implementación. Si un carácter de este conjunto se almacena en un objeto de carácter, el valor integral de ese objeto de carácter es igual al valor de la única forma literal de carácter de ese carácter. Es la implementación -define si un objeto char puede contener valores negativos . Los caracteres se pueden declarar explícitamente sin firmar o
firmado.Normal, firmada, y sin signo son tres tipos distintos. Un char, un char firmado y un char sin signo ocupan la misma cantidad de almacenamiento y tienen los mismos requisitos de alineación (basic.types); es decir, tienen la misma representación de objeto . Para los tipos de caracteres, todos los bits de la representación del objeto
participan en la representación del valor. Para los tipos de caracteres sin signo , todos los patrones de bits posibles de la representación del valor representan números. Estos requisitos no son válidos para otros tipos. En cualquier implementación particular, un objeto de carbonilla simple puede tomar con los mismos valores que un char firmado o un char sin signo; cuál es implementación definida.

Así análoga a esto es también la razón por la siguiente falla:

unsigned int* a = new unsigned int(10); 
int* b = static_cast<int*>(a); // error different types 

a y b son completamente diferentes tipos, realmente lo que usted está cuestionando es por ello que se static_cast tan restrictivo cuando se puede llevar a cabo las siguientes acciones sin problema

unsigned int a = new unsigned int(10); 
int b = static_cast<int>(a); // OK but may result in loss of precision 

y por qué no se puede deducir que los tipos de destino son el mismo ancho de campo de bits y pueden ser representados? Puede hacer esto para los tipos escalares pero para los punteros, a menos que el objetivo se derive de la fuente y desee realizar un downcast, entonces, el lanzamiento entre punteros no va a funcionar.

Bjarne Stroustrop indica por qué static_cast 's son útiles en este enlace: http://www.stroustrup.com/bs_faq2.html#static-cast pero en forma abreviada es que el usuario indique claramente cuáles son sus intenciones y para dar el compilador la oportunidad de comprobar que lo que se viene puede ser logrado, ya que static_cast no es compatible con la conversión entre diferentes tipos de punteros, entonces el compilador puede detectar este error para alertar al usuario y si realmente desea hacer esta conversión, entonces debe usar reinterpret_cast.

+0

thx para establecer el estándar aquí. No lo tengo disponible. –

+0

Entonces, si son tipos distintos, ¿por qué el compilador permite el molde 'unsigned char a = 255; char b = static_cast (a); '? – Nick

+2

la misma razón por la que puede static_cast de dobles a ints y, por otro lado, lo que no puede hacer es static_cast double * to int *, los tipos de puntero son diferentes pero puede convertir de un valor a otro con la advertencia de que puede haber ser una pérdida en la precisión – EdChum

7

está tratando de convertir punteros no relacionados con static_cast. Eso no es para lo que está static_cast. Aquí puede ver: Type Casting.

Con static_cast puede convertir datos numéricos (por ejemplo, char a unsigned char debería funcionar) o puntero a clases relacionadas (relacionadas por alguna herencia). Este no es el caso. Desea convertir un puntero no relacionado en otro, por lo que debe usar reinterpret_cast.

Básicamente, lo que estás tratando de hacer es que el compilador sea lo mismo que tratar de convertir un carácter * en un vacío *.


Bien, aquí algunos pensamientos adicionales por qué permitir esto es fundamentalmente erróneo. static_cast se puede usar para convertir tipos numéricos entre sí. Por lo que es perfectamente legal para escribir lo siguiente:

char x = 5; 
unsigned char y = static_cast<unsigned char>(x); 

lo que también es posible:

double d = 1.2; 
int i = static_cast<int>(d); 

Si nos fijamos en el código en ensamblador verá que el segundo molde no es una mera re -interpretación del patrón de bits de d pero en su lugar algunas instrucciones de ensamblador para conversiones se insertan aquí.

Ahora si ampliamos este comportamiento a las matrices, el caso en el que basta con una forma diferente de interpretar el patrón de bits, podría funcionar. Pero, ¿qué pasa con el lanzamiento de matrices de dobles en matrices de ints? Ahí es donde tienes que declarar que simplemente quieres una reinterpretación de los patrones de bits; hay un mecanismo para eso llamado reinterpret_cast, o debes hacer un trabajo extra. Como puede ver, la extensión simple de static_cast para puntero/matrices no es suficiente, ya que necesita comportarse de manera similar a los valores únicos de static_casting de los tipos. Esto a veces necesita un código adicional y no se puede definir claramente cómo se debe hacer para las matrices. En su caso, deteniéndose en \ 0, porque es la convención? Esto no es suficiente para casos que no sean cadenas (número). ¿Qué sucederá si el tamaño del tipo de datos cambia (por ejemplo, int frente a doble en x86-32bit)?

El comportamiento que desea no se puede definir correctamente para todos los casos de uso, es por eso que no está en el estándar de C++. De lo contrario, tendría que recordar cosas como: "puedo convertir este tipo en otro siempre que sean de tipo entero, tengan el mismo ancho y ...". De esta manera, es totalmente claro, ya sea que se trate de CLASES relacionadas, luego puede lanzar los indicadores, o son tipos numéricos, entonces puede emitir los valores.

+0

Reconocí en mi primer post que el compilador dice que son punteros no relacionados. Lo que quiero saber es _por qué_. Me parece que si 'T1' está" relacionado "con' T2', entonces 'T1 *' debe estar "relacionado" con 'T2 *'. ¿Por qué no suena esa regla de tipeo (para tipos primitivos)? – Nick

+3

@Nick no está "de alguna manera relacionado" sino "clases relacionadas". Como dijiste, char y unsigned char son primitivos, no clases. Esa es la razón, y eso es lo que dije si lees con cuidado. Tienes razón: si la clase T1 está relacionada con la clase T2, entonces puedes usar static_cast para convertir T1 * a T2 *. Esto no es lo que estás haciendo. char no está relacionado con char sin signo en el sentido de relación requerido por el estándar C++. –

+0

Sin embargo, si suelta el puntero, el compilador no tendrá problemas para el fundido entre los tipos primitivos, p. 'unsigned char a = 255; char b = static_cast (a); 'Parece un poco extraño, ya que si' T1' y 'T2' son clases, el molde entre los punteros no es correcto, ya que podría hacer algo como: ' class A; ' 'clase B: un público;' 'B * b = new B [4];' 'b [0] = B();' 'a * a = static_cast (b);' 'una [1] = A(); ' ' B b1 = b [1]; // oops' Parece que el _undo_ momento en que el yeso debería estar seguro se encuentra entre los tipos primitivos. – Nick

4

Aparte de ser punteros, unsigned char * y char * tienen nada en común (EdChum ya se ha mencionado el hecho de que char, signed char y unsigned char tres tipos diferentes). Podría decir lo mismo para Foo * y Bar * tipos de puntero a cualquier estructura diferente.

static_cast significa que un puntero del tipo de fuente se puede utilizar como un puntero del tipo de destino, que requiere una relación de subtipo. Por lo tanto, no puede usarse en el contexto de su pregunta; lo que necesita es reinterpret_cast que hace exactamente lo que quiere o un molde de estilo C.

Cuestiones relacionadas