Почему stringstream >> изменяет значение цели при ошибке?

От Stroustrup TС++ PL, 3-е издание, раздел 21.3.3:

Если мы попытаемся прочитать переменную v и операция завершится неудачно, значение v должно быть неизменным (оно не изменяется, если v является одним из типов, обрабатываемых функциями istream или ostream-членами).

Следующий пример, похоже, противоречит приведенной цитате. Основываясь на приведенной выше цитате, я ожидал, что значение v останется неизменным - но оно обнуляется. Какое объяснение этого очевидного противоречивого поведения?

#include <iostream>
#include <sstream>

int main( )
{
    std::stringstream  ss;

    ss  << "The quick brown fox.";

    int  v = 123;

    std::cout << "Before: " << v << "\n";

    if( ss >> v )
    {
        std::cout << "Strange -- was successful at reading a word into an int!\n";
    }

    std::cout << "After: " << v << "\n";

    if( ss.rdstate() & std::stringstream::eofbit  ) std::cout << "state: eofbit\n";
    if( ss.rdstate() & std::stringstream::failbit ) std::cout << "state: failbit\n";
    if( ss.rdstate() & std::stringstream::badbit  ) std::cout << "state: badbit\n";

    return 1;
}

Выход, который я получаю с помощью x86_64-w64-mingw32-g++. exe (rubenvb-4.7.2-release) 4.7.2:

Before: 123
After: 0
state: failbit

Спасибо.

Ответы

Ответ 1

От эта ссылка:

Если извлечение завершилось неудачно (например, если была введена буква, в которой ожидается цифра), значение остается неизмененным и устанавливается битбит (до С++ 11)

Если извлечение завершилось неудачно, нуль записывается в значение и устанавливается failbit. Если извлечения приводит к слишком большому или слишком маленькому значению, чтобы соответствовать значению, записывается std:: numeric_limits:: max() или std:: numeric_limits:: min() и устанавливается флаг failbit. (, поскольку С++ 11)

Кажется, что ваш компилятор компилируется в режиме С++ 11, что изменяет поведение.


Оператор ввода использует фасет локали std::num_get, функция get вызывает do_get. Для С++ 11 он указал, чтобы использовать std::strtoll et. и др. тип функций. До С++ 11 он, по-видимому, использовал std::scanf стиль синтаксического разбора (по ссылке, у меня нет доступа к С++ 03 спецификации) для извлечения чисел. Изменение поведения связано с этим изменением в анализе ввода.

Ответ 2

Оператор → - форматированный входной оператор.
Как таковой зависит от языка для того, как считывается ввод из потока:

[istream.formatted.arithmetic]

Как и в случае с вставками, эти экстракторы зависят от объекта locales num_get < > (22.4.2.1), чтобы выполнить синтаксический анализ данных входного потока. Эти экстракторы ведут себя как форматированные входные функции (как описано в 27.7.2.2.1). После создания часового объекта преобразование происходит, как если бы выполнялось следующим фрагментом кода:

   typedef num_get< charT,istreambuf_iterator<charT,traits> > numget;
   iostate err = iostate::goodbit;
   use_facet< numget >(loc).get(*this, 0, *this, err, val);
   setstate(err);

Как мы видим выше, значение фактически задается гранью numget языковой привязки, вложенной в поток.

виртуальные функции num_get [facet.num.get.virtuals]

Этап 3:

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

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

Определение этапа 3 резко изменилось между n2723 → n2798

Где найти текущие стандартные документы C или С++?

виртуальные функции num_get [facet.num.get.virtuals]

Этап 3: результат обработки этапа 2 может быть одним из:

  • На этапе 2 была скопирована последовательность символов, которая преобразуется (согласно правилам scanf) в значение типа val. Это значение сохраняется в val и ios_base:: goodbit хранится в err.
  • Последовательность символов, накопленных на этапе 2, заставила scanf сообщать о сбое ввода. ios_base:: failbit присваивается ошибке.