std :: cout имеет дело с uint8_t как персонажем

Если я запускаю этот код:

std::cout << static_cast<uint8_t>(65);

Это выведет:

Что является эквивалентом ASCII числа 65.
Это потому что uint8_t просто определяется как:

typedef unsigned char uint8_t;
  • Это стандартное поведение?

  • Не должно быть лучшего способа определить uint8_t что гарантированно будет рассматриваться как число, а не как символ?

Я не могу понять логику, что если я хочу напечатать значение uint8_t переменная, она будет напечатана как символ.

Постскриптум Я использую MSVS 2013.

4

Решение

Публикация ответа, поскольку в комментариях есть некоторая дезинформация.

uint8_t может или не может быть typedef для char или же unsigned char, Также возможно, чтобы это был расширенный целочисленный тип (и, следовательно, не символьный тип).

Компиляторы могут предлагать другие целочисленные типы помимо минимального набора, требуемого стандартом (short, int, long, так далее). Например, некоторые компиляторы предлагают 128-битный целочисленный тип.

Это также не будет «конфликтовать с C», поскольку C и C ++ допускают расширенные целочисленные типы.

Итак, ваш код должен учитывать обе возможности. Предложение в комментариях использования одинарного + должно сработать.

Лично я думаю, что было бы более разумно, если бы стандарт требовал uint8_t в не быть типом персонажа, так как поведение, которое вы заметили, не интуитивно понятно.

3

Другие решения

Это косвенно стандартное поведение, потому что ostream имеет перегрузку для unsigned char а также unsigned char является typedef для того же типа uint8_t в вашей системе.

§27.7.3.1 [output.streams.ostream] дает:

template<class traits>
basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>&, unsigned char);

Я не мог найти нигде в стандарте, который явно заявил, что uint8_t а также unsigned char имел быть таким же, хотя. Просто разумно, что они занимают 1 байт почти во всех реализациях.

 std::cout << std::boolalpha << std::is_same<uint8_t, unsigned char>::value << std::endl; // prints true

Чтобы получить значение для печати в виде целого числа, вам нужен тип, который не unsigned char (или одна из других перегрузок символов). Вероятно, простое приведение к uint16_t достаточно, потому что стандарт не перечисляет перегрузку для него:

uint8_t a = 65;
std::cout << static_cast<uint16_t>(a) << std::endl; // prints 65

демонстрация

3

Стандартное ли это поведение?

Поведение стандартно в том, что если uint8_t является определением типа unsigned char тогда он всегда будет печатать символ как std::ostream имеет перегрузку для unsigned char и распечатывает содержимое переменной в виде символа.

Не должно быть лучшего способа определить uint8_t что гарантированно будет рассматриваться как число, а не как символ?

Чтобы сделать это, комитет C ++ должен был бы ввести новый фундаментальный тип. В настоящее время единственные типы, которые имеют sizeof() что равно 1 char, signed char, а также unsigned char, Возможно, они могли бы использовать bool но bool не должен иметь размер 1, а затем вы все еще в той же лодке, так как

int main()
{
bool foo = 42;
std::cout << foo << '\n';
}

распечатает 1не 42 поскольку любое ненулевое значение true, а true печатается как 1 но по умолчанию.

Я не говорю, что это не может быть сделано, но это много работы для чего-то, что может быть обработано с помощью актерский состав или функция


C ++ 17 вводит std::byte который определяется как enum class byte : unsigned char {};, Так что он будет шириной в один байт, но это не символьный тип. К сожалению, так как это enum class это идет со своими собственными ограничениями. Для него были определены побитовые операторы, но для него нет встроенных потоковых операторов, поэтому вам нужно определить свои собственные для ввода и вывода. Это означает, что вы все еще конвертируете его, но по крайней мере вы не будете конфликтовать со встроенными операторами для unsigned char, Это дает вам что-то вроде

std::ostream& operator <<(std::ostream& os, std::byte b)
{
return os << std::to_integer<unsigned int>(b);
}

std::istream& operator <<(std::istream& is, std::byte& b)
{
unsigned int temp;
is >> temp;
b = std::byte{b};
return is;
}

int main()
{
std::byte foo{10};
std::cout << foo;
}
3