Как проверить, подписаны ли простые символы или нет?

По-видимому, существует вероятность, что обычный char может быть либо подписан, либо неподписанным по умолчанию. Страуступ пишет:

Определяется реализацией, считается ли простой char подписанным или неподписанным. Это открывает возможность для некоторых неприятных сюрпризов и зависимостей реализации.

Как проверить, подписаны ли мои символы или нет? Возможно, мне захочется впоследствии преобразовать их в int, и я не хочу, чтобы они были отрицательными. Должен ли я всегда использовать unsigned char явно?

Ответ 1

Некоторые альтернативы:

const bool char_is_signed = (char)-1 < 0;

#include <climits>
const bool char_is_signed = CHAR_MIN < 0;

И да, некоторые системы делают простой char неподписанным. Примеры, с которыми я столкнулся: Cray T90, Cray SV1, Cray T3E, SGI MIPS IRIX, IBM PowerPC AIX. И любая система, использующая EBCDIC, должна сделать простой char неподписанным, чтобы все основные символы имели неотрицательные значения. (И некоторые компиляторы имеют возможность контролировать подпись char, например gcc -fsigned-char и -funsigned-char.)

Но std::numeric_limits<char>::is_signed, как было предложено ответом Бенджамина Линдли, возможно, более четко выражает намерение.

(С другой стороны, предложенные мной методы также могут быть применены к C.)

Ответ 3

Использование unsigned char "всегда" может дать вам интересные сюрпризы, так как большинство функций стиля C, таких как printf, fopen, будут использовать char, а не unsigned char.

edit: Пример "fun" с функциями C-стиля:

const unsigned char *cmd = "grep -r blah *.txt";
FILE *pf = popen(cmd, "r"); 

даст ошибки (фактически, я получаю один для строки *cmd = и одну ошибку для строки popen). Использование const char *cmd = ... будет работать нормально. Я выбрал popen, потому что это функция, которая не является тривиальной для замены некоторыми стандартными функциональными возможностями на С++ - очевидно, что printf или fopen может быть легко заменена некоторыми функциями типа iostream или fstream, которые обычно имеет альтернативы, которые принимают unsigned char, а также char.

Однако, если вы используете > или < для символов, которые находятся за пределами 127, вам нужно будет использовать unsigned char (или какое-то другое решение, например, отличное от int) и маскирование нижнего 8 биты). Вероятно, лучше попытаться избежать прямых сравнений (в частности, когда речь заходит о символах, отличных от ASCII), они в любом случае беспорядочны, потому что часто бывает несколько вариантов в зависимости от языка, кодировки символов и т.д.). Однако сравнение для равенства должно работать.

Ответ 4

Да, если вы хотите использовать тип char, и вы всегда хотите, чтобы он был неподписанным, используйте unsigned char. Обратите внимание, что в отличие от других основных целых типов unsigned char является другим типом от char - даже в системах, где char не имеет знака. Кроме того, преобразование с char в int должно быть без потерь, поэтому, если ваш результат неверен, значение источника char также может быть неверным.

Самый чистый способ проверить, является ли char неподписанным, зависит от того, нужен ли вам это тест препроцессора и на какой версии С++ вы нацеливаетесь.

Чтобы условно скомпилировать код с использованием теста препроцессора, значение CHAR_MIN должно работать:

#include <climits>

#if (CHAR_MIN==0)
// code that relies on char being unsigned
#endif

В С++ 17 я бы использовал std::is_signed_v и std::is_unsigned_v:

#include <type_traits>

static_assert(std::is_unsigned_v<char>);
// code that relies on char being unsigned

Если вы пишете на С++ 11 или С++ 14, вам нужно немного более подробное std::is_signed и std::is_unsigned:

#include <type_traits>

static_assert(std::is_unsigned<char>::value, "char is signed");
// code that relies on char being unsigned

Для всех версий С++ решение benjamin-lindley является хорошей альтернативой.

Ответ 5

Вы можете использовать команду препроцессора:

 #define is_type_signed(my_type) (((my_type)-1) < 0)