Значение сохраняется при сбое чтения istream

Образец кода:

#include <iostream>

int main()
{
    int x = 5;
    std::cin >> x;
    std::cout << x << '\n';
}

В одной конкретной реализации происходит следующее поведение:

  • Вход: 6; выход 6
  • Ввод: a; вывод: 0
  • Ввод: (конец файла); выход 5
  • Ввод: (пробел, за которым следует конец файла); выход 5

Таким образом, в случае ошибки cin >> x присваивает 0 значение x, если не удалось преобразовать текст в int; но он не назначает 0, если сбой произошел из-за конца файла.

Это правильное поведение? Если нет, то каково правильное поведение в соответствии со стандартом С++?

У меня есть воспоминание из предыдущего обсуждения на SO, что все случаи должны писать 0 начиная с С++ 11, но я не смог ничего найти с помощью функции поиска; и раздел iostreams стандарта С++ довольно тяжелый.


person M.M    schedule 05.10.2015    source источник


Ответы (2)


Согласно параграфу 1 27.7.2.2.1 [istream.formatted.reqmts], первое, что нужно сделать для функции форматированного ввода, — создать объект std::istream::senty. Дальнейшая обработка зависит от того, преобразуется ли этот объект в true или false: ничего не происходит со значением, если sentry преобразуется в false.

Согласно 27.7.2.1.3 [istream::sentry], параграфы 5 и 7, sentry преобразуется в false, если флаги потока не равны std::ios_base::goodbit. То есть, если произойдет сбой или будет достигнут EOF, sentry преобразуется в false. В результате value остается на 5, когда EOF достигается после пропуска пробелов, при условии, что установлено std::ios_base::skipws. Сброс std::ios_base::skipws должен привести к тому, что значение станет 0, если есть хотя бы один пробел.

После фактического синтаксического анализа логика применения определяется в 22.4.2.1.2 [facet.num.get.virtuals], параграф 3, этап 3. Ключевым разделом затронутого значения является

...

Сохраняемое числовое значение может быть одним из следующих:

— ноль, если функция преобразования не может преобразовать все поле. ios_base::failbit назначается err.

— наиболее положительное представимое значение, если поле представляет слишком большое положительное значение, чтобы быть представленным в val. ios_base::failbit назначается err.

— самое отрицательное представимое значение или ноль для беззнакового целочисленного типа, если поле представляет слишком большое отрицательное значение, чтобы быть представленным в val. ios_base::failbit назначается err.

— преобразованное значение, в противном случае.

Результирующее числовое значение сохраняется в val.

Таким образом, наблюдаемое поведение является правильным.

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

То, что значение не изменяется, если EOF достигнут до попытки преобразования, может считаться ошибкой. Я не припоминаю, чтобы этот случай рассматривался при обсуждении изменения.

person Dietmar Kühl    schedule 05.10.2015

Да, это правильное поведение, начиная с С++ 11.

Разница в том, что вы видите, заключается в том, что ноль записывается «при сбое извлечения», но извлечение даже не предпринимается, если EOF уже установлен в потоке, поэтому ничего не происходит.

person Lightness Races in Orbit    schedule 05.10.2015