Почему 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 как тип символа, а не целое число), требуемый стандартом?
Если это тогда:
- В чем обоснование такой противоречивой семантики?
- Если это считается дефектом, были ли предложения по изменению этой семантики?
Вероятно, я должен добавить небольшое объяснение, почему я считаю его нелогичным.
Хотя имя типа содержит слово 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.
- В чем обоснование такой противоречивой семантики?
Потому что signed char
и unsigned char
являются символьными типами, поэтому они всегда рассматриваются как символы классами iostreams.
Ключ находится в имени: signed char
- это тип подписанного символа. unsigned char
- это неподписанный тип символа. Другие интегральные типы имеют int
в их имени (даже если оно иногда необязательно, например, short
и long unsigned
идентичны short int
и long unsigned int
соответственно).
Стандарт не должен говорить, почему это так, потому что это не дизайнерский документ или обоснование истории C и С++, это спецификация.
Если вам нужен тип, который ведет себя как целое число с 8 битами, тогда вам нужно будет создать свой собственный (например, используя тип перечисления или структуру, содержащую значение) и определить соответствующие перегрузки оператора.
- Если это считается дефектом, были ли предложения по изменению этой семантики?
Нет, я так не думаю. Они всегда были типами символов, и это могло бы сломать слишком много кода, чтобы изменить это.