Почему извлечения потока из потока задает бит eof?

Скажем, у нас есть поток, содержащий просто:

hello

Обратите внимание, что в конце нет лишнего \n, как это часто бывает в текстовом файле. Теперь следующий простой код показывает, что бит eof устанавливается в потоке после извлечения одного std::string.

int main(int argc, const char* argv[])
{
  std::stringstream ss("hello");
  std::string result;
  ss >> result;
  std::cout << ss.eof() << std::endl; // Outputs 1
  return 0;
}

Однако я не понимаю, почему это произойдет в соответствии со стандартом (я читаю С++ 11 - ISO/IEC 14882: 2011 (E)). operator>>(basic_stream<...>&, basic_string<...>&) определяется как поведение как форматированная входная функция. Это означает, что он создает объект sentry, который переходит к пропуску пробельных символов. В этом примере их нет, поэтому конструкция sentry завершается без проблем. При преобразовании в bool объект sentry дает true, поэтому экстрактор продолжает работать с фактическим извлечением строки.

Затем извлечение определяется как:

Символы извлекаются и добавляются до тех пор, пока не произойдет следующее:

  • n сохраняются символы
  • конец файла происходит во входной последовательности;
  • isspace(c,is.getloc()) верен для следующего доступного входного символа c.

После того, как последний символ (если он есть) извлечен, вызывается is.width(0), и сторожевой объект k уничтожается. Если функция не извлекает никаких символов, она вызывает is.setstate(ios::failbit), которая может бросать ios_base::failure (27.5.5.4).

Ничто здесь не приводит к установке бит eof. Да, извлечение останавливается, если оно попадает в конец файла, но оно не устанавливает бит. Фактически бит eof должен быть установлен только в том случае, если мы делаем еще один ss >> result;, потому что, когда sentry пытается сохнуть пробелы, произойдет следующая ситуация:

Если is.rdbuf()->sbumpc() или is.rdbuf()->sgetc() возвращает traits::eof(), функция вызывает setstate(failbit | eofbit)

Однако это пока еще не происходит, поскольку failbit не устанавливается.

Следствие установленного бита eof заключается в том, что единственная причина, по которой evil-idiom while (!stream.eof()) не работает при чтении файлов из-за дополнительного \n в конце, а не потому, что eof бит еще не установлен. Мой компилятор с радостью устанавливает бит eof, когда извлечения останавливается в конце файла.

Так должно ли это происходить? Или стандартный означает сказать, что setstate(eofbit) должен произойти?


Чтобы сделать это проще, соответствующие разделы стандарта:

  • 21.4.8.9 Вставки и экстракторы [string.io]
  • 27.7.2.2 Форматированные входные функции [istream.formatted]
  • 27.7.2.1.3 Класс basic_istream::sentry [istream:: sentry]

Ответы

Ответ 1

std::stringstream является basic_istream, а operator>> из std::string "извлекает" из него символы (как вы узнали).

27.7.2.1 Шаблон класса basic_istream

2 Если rdbuf() → sbumpc() или rdbuf() → sgetc() возвращает черты:: eof(), то входная функция, кроме как явно указано иначе, завершает свои действия и устанавливает setstate (eofbit), которые могут вызывать ios_- base:: failure (27.5.5.4), перед возвратом.

Кроме того, "извлечение" означает вызов этих двух функций.

3 Две группы сигнатур функций-членов имеют общие свойства: форматированные входные функции (или экстракторы) и неформатированные входные функции. Обе группы входных функций описываются так, как если бы они получить (или извлечь) входные символы, вызвав rdbuf() → sbumpc() или rdbuf() → sgetc(). Они могут использовать другие общественные члены istream.

Поэтому необходимо установить eof.

Ответ 2

Интуитивно говоря, бит EOF устанавливается, потому что во время операции чтения для извлечения строки поток действительно попал в конец файла. В частности, он непрерывно считывает символы из входного потока, останавливаясь, потому что он попадает в конец потока, прежде чем сталкивается с символом пробела. Соответственно, поток установил бит EOF, чтобы отметить, что достигнут конец потока. Обратите внимание, что это не то же самое, что сообщение об ошибке - операция была успешно завершена, но точка бит EOF не сообщает о сбое. Это означало, что был встречен конец потока.

У меня нет определенной части спецификации, чтобы поддержать это, хотя я попытаюсь найти его, когда я получу шанс.

Надеюсь, это поможет!