Почему 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 присваивается ошибке.