2009-01-12 27 views
35

El siguiente código compila, pero tiene un comportamiento diferente para el tipo de caracteres que para los tipos int.char! = (Char firmado), char! = (Char no firmado)

En particular

cout << getIsTrue< isX<int8>::ikIsX >() << endl; 
    cout << getIsTrue< isX<uint8>::ikIsX >() << endl; 
    cout << getIsTrue< isX<char>::ikIsX >() << endl; 

resultado en 3 instanciaciones de plantillas para tres tipos: int8, uint8 y Char. ¿Lo que da?

Lo mismo no es cierto para ints: int y uint32 que dan como resultado la misma instanciación de plantilla, y se firman en otro int.

La razón parece ser que C++ ve char, signed char y unsigned char como tres tipos diferentes. Mientras que int es lo mismo que un int firmado. ¿Es correcto o me estoy perdiendo algo?

#include <iostream> 

using namespace std; 

typedef signed char  int8; 
typedef unsigned char  uint8; 
typedef signed short  int16; 
typedef unsigned short  uint16; 
typedef signed int  int32; 
typedef unsigned int  uint32; 
typedef signed long long int64; 
typedef unsigned long long uint64; 

struct TrueType {}; 
struct FalseType {}; 

template <typename T> 
struct isX 
{ 
    typedef typename T::ikIsX ikIsX; 
}; 


// This int==int32 is ambiguous 
//template <>   struct isX<int > { typedef FalseType ikIsX; }; // Fails 
template <>   struct isX<int32 > { typedef FalseType ikIsX; }; 
template <>   struct isX<uint32 > { typedef FalseType ikIsX; }; 


// Whay isn't this ambiguous? char==int8 
template <>   struct isX<char > { typedef FalseType ikIsX; }; 
template <>   struct isX<int8 > { typedef FalseType ikIsX; }; 
template <>   struct isX<uint8 > { typedef FalseType ikIsX; }; 


template <typename T> bool getIsTrue(); 
template <>   bool getIsTrue<TrueType>() { return true; } 
template <>   bool getIsTrue<FalseType>() { return false; } 

int main(int, char **t) 
{ 
    cout << sizeof(int8) << endl; // 1 
    cout << sizeof(uint8) << endl; // 1 
    cout << sizeof(char) << endl; // 1 

    cout << getIsTrue< isX<int8>::ikIsX >() << endl; 
    cout << getIsTrue< isX<uint8>::ikIsX >() << endl; 
    cout << getIsTrue< isX<char>::ikIsX >() << endl; 

    cout << getIsTrue< isX<int32>::ikIsX >() << endl; 
    cout << getIsTrue< isX<uint32>::ikIsX >() << endl; 
    cout << getIsTrue< isX<int>::ikIsX >() << endl; 

} 

estoy usando g ++ 4.Algo

+0

También debe tomar nota de que no hay garantía de que 'int8_t' va a ser un' 'char' firmado y uint8_t' va a ser un' char' sin firmar. En particular, en Solaris 'int8_t' es simplemente' char' si 'char' está firmado. En otras palabras, su código no podrá compilar allí. –

+0

"int y uint32 que da como resultado la misma instanciación de la plantilla, y firmado en otro int", esto definitivamente debería ser al revés, ya que int está firmado. –

Respuesta

55

Aquí está la respuesta de la norma:

3.9.1 tipos fundamentales [basic.fundamental]

objetos declarados como caracteres (char) tendrán que ser suficientemente grande para almacenar cualquier miembro de la aplicación de conjunto de caracteres básicos Si un personaje 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 forma literal de un solo carácter de ese carácter. Se define en la implementación si un objeto char puede contener valores negativos. Los caracteres se pueden declarar explícitamente unsigned o signed. Normal char, signed char y unsigned char son tres tipos distintos. A char, signed char, y unsigned char 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 de , un objeto simple char puede tomar los mismos valores que signed char o unsigned char; cuál es definido por la implementación.

13

que es correcto, char, unsigned char y signed char son tipos distintos. Probablemente hubiera estado bien si char fuera solo un sinónimo de signed char o unsigned char dependiendo de la implementación de su compilador, pero el estándar dice que son tipos separados.

16

Mientras que la mayoría de los tipos integrales como short y int por defecto a ser signed, char no tiene una señalización por defecto en C++.

Es un error común que los programadores de C++ se topan cuando usan char como un tipo entero de 8 bits.

+2

+1 para comparar también con short e int. – imallett

+0

+1 porque explica muy sucintamente las diferencias en los tipos de datos e implica cómo deberían usarse en comparación. – Peri461

20

Para preguntas como esta, me gusta consultar el documento de Justificación para C, que a menudo también proporciona respuestas a misterios de C++, que a veces surgen cuando leo el Estándar. Tiene esto que decir al respecto:

Se especifican tres tipos de caracteres: firmado, normal y sin signo. Un char simple puede representarse como firmado o no, dependiendo de la implementación, como en la práctica anterior. El tipo char firmado se introdujo para poner a disposición un tipo entero con signo de un byte en los sistemas que implementan el carácter simple como sin signo. Por razones de simetría, la palabra clave signed está permitida como parte del nombre de tipo de otros tipos integrales.

Rationale for C

+0

Entonces, ¿por qué necesitamos 'signed char'? ¿Solo para usarlo para representar un entero con signo de un byte? – Alcott

+1

@Alcott, creo que 'char' podría estar firmado, o podría estar sin firmar, que es implementación definida, pero' signed char' siempre está firmado, y 'unsigned char' está siempre sin firmar, si quieres estar seguro/explícito del tipo – hanshenrik

Cuestiones relacionadas