char! = (подписанный символ), символ! = (без знака) - PullRequest
37 голосов
/ 12 января 2009

Код ниже компилируется, но имеет другое поведение для типа char, чем для типов int.

В частности

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

приводит к 3 экземплярам шаблонов для трех типов: int8, uint8 и char. Что дает?

То же самое не верно для целых: int и uint32, которые приводят к одному и тому же экземпляру шаблона и подписываются в другом.

Причина, по-видимому, в том, что C ++ рассматривает char, подписанный char и unsigned char как три разных типа. Принимая во внимание, что int - это то же самое, что и int со знаком. Это правильно или я что-то упустил?

#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;

}

Я использую g ++ 4. что-то

Ответы [ 4 ]

59 голосов
/ 12 января 2009

Вот ваш ответ от стандарта:

3.9.1 Фундаментальные типы [basic.fundamental]

Объекты, объявленные как символы (char), должны быть достаточно большими, чтобы хранить любой элемент базового набора символов реализации. Если символ из этого набора хранится в символьном объекте, интегральное значение этого символьного объекта равно значению буквальной формы одного символа этого символа. Это определяется реализацией, может ли объект char содержать отрицательные значения. Символы могут быть явно объявлены unsigned или signed. Обычный char, signed char и unsigned char - это три различных типа. A char, signed char и unsigned char занимают одинаковое количество хранение и имеют одинаковые требования к выравниванию ( basic.types ); то есть они имеют одинаковое объектное представление. Для символьных типов все биты представления объекта участвуют в представлении значения. Для типов символов без знака все возможные битовые комбинации представления значения представляют собой числа. Эти требования не распространяются на другие типы. В любом конкретном В реализации простой объект char может принимать либо те же значения, что и signed char, либо unsigned char; какой из них определяется реализацией.

22 голосов
/ 12 января 2009

По таким вопросам, как этот, я хотел бы заглянуть в документ Rationale для C, который также часто дает ответы на загадки C ++, которые иногда возникают у меня при чтении Стандарта. Это говорит об этом:

Указаны три типа символов: подписанный, простой и без знака. Простой символ может быть представлен как подписанный или неподписанный, в зависимости от реализации, как в предыдущей практике. Тип подписанного символа был введен, чтобы сделать доступным однобайтовый целочисленный тип со знаком в тех системах, которые реализуют обычный символ как неподписанный. По соображениям симметрии ключевое слово подписывается как часть имени типа других целочисленных типов.

Обоснование для C

21 голосов
/ 12 января 2009

Хотя большинство целочисленных типов, таких как short и int, по умолчанию равны signed, char не имеет обозначения по умолчанию в C ++.

Это распространенная ошибка, с которой сталкиваются программисты C ++, когда они используют char как 8-битный целочисленный тип.

14 голосов
/ 12 января 2009

это правильно, char, unsigned char и signed char - это отдельные типы. Вероятно, было бы неплохо, если бы char был просто синонимом либо signed char, либо unsigned char, в зависимости от реализации вашей компиляции, но стандарт говорит, что они являются отдельными типами.

...