char! = (char signé), char! = (char non signé)
Question
Le code ci-dessous est compilé, mais le comportement du type char est différent de celui des types int.
En particulier
cout << getIsTrue< isX<int8>::ikIsX >() << endl;
cout << getIsTrue< isX<uint8>::ikIsX >() << endl;
cout << getIsTrue< isX<char>::ikIsX >() << endl;
résulte en 3 instanciations de modèles pour trois types: int8, uint8 et char. Qu'est-ce qui donne?
Il n'en va pas de même pour ints: int et uint32, qui entraînent la même instanciation du modèle et sont signés par un autre.
La raison semble être que C ++ considère char, sign char et unsigned char comme trois types différents. Alors que int est identique à un int signé. Est-ce vrai ou est-ce que je manque quelque chose?
#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;
}
J'utilise g ++ 4. quelque chose
La solution
Voici votre réponse à partir de la norme:
3.9.1 Types fondamentaux [basic.fundamental]
Les objets déclarés en tant que caractères (
char
) doivent être suffisamment grands pour stocker tout membre du jeu de caractères de base de la mise en oeuvre. Si un caractère de cet ensemble est stocké dans un objet caractère, la valeur intégrale de cet objet caractère est égale à la valeur de la forme littérale à caractère unique de ce caractère. L'implémentation est définie si un objetchar
peut contenir des valeurs négatives. Les caractères peuvent être explicitement déclarésunsigned
ousigned
. Plaincaractère
,caractère signé
etcaractère non signé
sont trois types distincts. Achar
, unchar signé
et uncharité non signé
occupent la même quantité de mémoire et ont les mêmes exigences en matière d'alignement ( basic.types ); c'est-à-dire qu'ils ont la même représentation d'objet. Pour les types de caractère, tous les bits de la représentation d'objet participent à la représentation de valeur. Pour les types de caractères non signés, tous les modèles de bits possibles de la représentation de valeur représentent des nombres. Ces exigences ne sont pas valables pour les autres types. En tout particulier Lors de la mise en œuvre, un objetchar
peut prendre les mêmes valeurs qu’unchar signé
ou uncharité non signé
; lequel est défini par l'implémentation.
Autres conseils
Pour des questions comme celle-ci, j’aimerais examiner le document de justification pour C, qui fournit souvent aussi des réponses aux mystères de C ++, qui m’arrive parfois lorsque je lis la norme. Voici ce qu’il en dit:
Trois types de caractères sont spécifiés: signé, brut et non signé. Un caractère simple peut être représenté comme signé ou non signé, en fonction de la mise en œuvre, comme dans la pratique antérieure. Le type signé du caractère a été introduit pour rendre disponible un type entier signé sur un octet sur les systèmes qui implémentent le caractère brut en tant que non signé. Pour des raisons de symétrie, le mot-clé signé est autorisé dans le nom de type d'autres types d'intégrale.
Bien que la plupart des types intégraux tels que short
et int
soient signés
, char
n'a pas de valeur par défaut. signalisation en C ++.
C'est une erreur courante que rencontrent les programmeurs C ++ lorsqu'ils utilisent char
en tant que type entier 8 bits.
c'est correct, char
, char non signé
et char signé
sont des types séparés. Cela aurait probablement été bien si char
était simplement synonyme de char signé
ou de char non signé
en fonction de l'implémentation de vos compilateurs, mais la norme dit ce sont des types séparés.