Почему std::( ​​i) ostream обрабатывает подписанный/неподписанный char как текст, а не целое число?

Этот код не выполняет то, что он должен делать:

#include <iostream>
#include <cstdint>

int main()
{
    uint8_t small_integer;
    std::cin >> small_integer;
    std::cout << small_integer;
}

Причина проста: uint8_t является typedef для unsigned char, а потоки рассматривают этот тип как текст:
Внедрение Visual С++ 2015

template<class _Traits> inline
    basic_istream<char, _Traits>& operator>>(
        basic_istream<char, _Traits>& _Istr, unsigned char& _Ch)
    {    // extract an unsigned char
    return (_Istr >> (char&)_Ch);
    }

И аналогичный код с литой до char для operator <<.

Мои вопросы:

  • Это поведение (потоковые операторы, обрабатывающие подписанный /unsigned char как тип символа, а не целое число), требуемый стандартом? Если это тогда:
    1. В чем обоснование такой противоречивой семантики?
    2. Если это считается дефектом, были ли предложения по изменению этой семантики?

Вероятно, я должен добавить небольшое объяснение, почему я считаю его нелогичным. Хотя имя типа содержит слово char, часть signed или unsigned указывает конкретную целую семантику, и эти типы обычно используются как целые числа в байтах. Даже стандарт определяет через них int8_t/uint8_t.

UPD: Вопрос о поведении перегрузок операторов потоковой передачи для unsigned char и signed char.

Ответ 1

В стандарте (n3797) говорится следующее:

27.7.2.2.3 basic_istream:: operator →

template<class charT, class traits> 
basic_istream<charT,traits>& operator>>(basic_istream<charT,traits>& in, charT& c);

template<class traits> 
basic_istream<char,traits>& operator>>(basic_istream<char,traits>& in, unsigned char& c);

template<class traits> 
basic_istream<char,traits>& operator>>(basic_istream<char,traits>& in, signed char& c);

12 E ff ects: ведет себя как форматированный входной элемент (как описано в 27.7.2.2.1) of. После того, как объект-сторожевой объект сконструирован , символ извлекается из, если он доступен, и хранится в c. В противном случае функция вызывает in.setstate(failbit).

27.7.3.6.4 Шаблоны функций вставки символов

// specialization 
template<class traits> 
basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, char c); 

// signed and unsigned 
template<class traits> 
basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, signed char c); 

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

1 E ff ects: ведет себя как форматированная выходная функция (27.7.3.6.1) out. Построит последовательность символов seq. Если c имеет тип char, а тип символа потока не char, то seq состоит из out.widen(c); иначе seq состоит из c. Определяет заполнение для seq, как описано в 27.7.3.6.1. Вставляет seq в выход. Вызывает os.width(0).

Итак, ответ на первый вопрос: да, стандарт требует, чтобы operator >> и operator << вел себя одинаково для char, unsigned char и signed char, то есть они читают/записывают один символ, а не целое число. К сожалению, стандарт не объясняет почему. Я надеюсь, что кто-то прольет свет на 2 и 3.

Ответ 2

  • Требуется ли такое поведение стандарту? Если это так:

Вы уже ответили на это. Да, стандарт определяет, как iostreams должны обрабатывать подписанные и unsigned char.

  1. В чем обоснование такой противоречивой семантики?

Потому что signed char и unsigned char являются символьными типами, поэтому они всегда рассматриваются как символы классами iostreams.

Ключ находится в имени: signed char - это тип подписанного символа. unsigned char - это неподписанный тип символа. Другие интегральные типы имеют int в их имени (даже если оно иногда необязательно, например, short и long unsigned идентичны short int и long unsigned int соответственно).

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

Если вам нужен тип, который ведет себя как целое число с 8 битами, тогда вам нужно будет создать свой собственный (например, используя тип перечисления или структуру, содержащую значение) и определить соответствующие перегрузки оператора.

  1. Если это считается дефектом, были ли предложения по изменению этой семантики?

Нет, я так не думаю. Они всегда были типами символов, и это могло бы сломать слишком много кода, чтобы изменить это.