Символы, извлеченные istream> double

пример кода на Coliru:

#include <iostream>
#include <sstream>
#include <string>

int main()
{
    double d; std::string s;

    std::istringstream iss("234cdefipxngh");
    iss >> d;
    iss.clear();
    iss >> s;
    std::cout << d << ", '" << s << "'n";
}

я читаю N3337 здесь (предположительно, это то же самое, что и C++11). In [istream.форматированный.арифметика] у нас (перефразировано):

operator>>(double& val);

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

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);

глядя на 22.4.2.1:

детали этой операции происходят в три этапа
- Этап 1: Определите спецификатор преобразования
- Этап 2: извлечение символов из in и определение соответствующего значения char для формата ожидается спецификацией преобразования, определенной на этапе 1.
- Этап 3: хранить результаты

в описании этапа 2, это слишком долго для меня, чтобы вставить все это здесь. Однако он ясно говорит, что все символы должны быть извлечены до попытки преобразования; и далее, что точно следующие символы должны быть извлечены:

  • любой 0123456789abcdefxABCDEFX+-
  • стандарта decimal_point()
  • стандарта thousands_sep()

наконец, правила для Этапа 3 включают в себя:

- для значения с плавающей запятой функция strtold.

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

- ноль, если функция преобразования не может преобразовать все поле.

все это, кажется, четко указывает, что вывод моего кода должен быть 0, 'ipxngh'. Тем не менее, это на самом деле выводит что-то другое.

это ошибка компилятора / библиотеки? Есть ли какое-либо положение, которое я пропускаю для локали, чтобы изменить поведение этапа 2? (In еще вопрос кто-то опубликовал пример системы, которая действительно извлекает символы, но также извлекает ipxn которых нет в списке, указанном в N3337).

обновление

как отметил perreal, этот текст из Этапа 2 актуален:

если сбросить верно, то если'.'еще не было накоплено, то положение персонажа запоминается, но символ в противном случае игнорируется. В противном случае, если ’. уже накопленный, персонаж отбрасывается, и Этап 2 завершается. Если не отброшены, то проверка, чтобы определить, если c допускается в качестве следующего символа входного поля спецификатора преобразования, возвращаемого этапом 1. Если это так,то оно накапливается.

если символ либо отбрасывается, либо накапливается, затем расширяется ++in и обработка возвращается к началу этапа 2.

Итак, Этап 2 может завершиться, если символ находится в списке разрешенных символов, но не является допустимым символом для %g. Он не говорит точно, но, по-видимому, это относится к определению fscanf от C99, который позволяет:

  • непустая последовательность десятичных цифр, дополнительно содержащий десятичная точка символ, затем необязательная экспоненциальная часть, как определено в 6.4.4.2;
  • a 0x или 0X, затем непустая последовательность шестнадцатеричных цифр, необязательно содержащая десятичный символ, затем необязательная двоичная экспонента, как определено в 6.4.4.2;
  • INF или бесконечность, игнорируя case
  • NAN или NAN (N-char-последовательность opt), игнорируя случай в части NAN, где:

и

In кроме локали "C", могут быть приняты дополнительные формы последовательности субъекта, специфичные для локали.

Итак, на самом деле вывод Coliru правильный; и на самом деле обработка должны попытка проверить последовательность символов, извлеченных до допустимого ввода %g, при извлечении каждого персонажа.

следующий вопрос: Разрешено ли, как в потоке, с которым я связался ранее, принимать i , n, p etc на этапе 2?

это допустимые символы для %g, однако они не находятся в списке атомов, который Этап 2 разрешается читать (т. е. c == 0 для моей последней цитаты, поэтому персонаж не отбрасывается и не накапливается).

2 ответов


это беспорядок, потому что, вероятно, ни реализация gcc/libstdc++, ни Clang/libc++не соответствуют. Неясно," проверка производится для определения того, разрешен ли c в качестве следующего символа входного поля спецификатора преобразования, возвращаемого этапом 1", но я думаю, что использование фразы "следующий символ" указывает на то, что проверка должна быть контекстно-чувствительной (т. е. зависеть от символов, которые уже были накоплены), и поэтому попытка анализа, например, "21abc", следует прекратить, когда 'a' встречаются. Это согласуется с обсуждением в LWG выпуск 2041, который добавил Это предложение обратно в стандарт после того, как оно было исключено во время разработки C++11. неспособность libc++сделать это ошибка 17782.

libstdc++, с другой стороны, отказывается анализировать "0xABp-4" мимо 0, который на самом деле явно не соответствует стандарту (он должен анализировать "0xAB" как hexfloat, как ясно разрешено C99 fscanf спецификация %g).

прием i, p и n не допускается стандартом. См.LWG выпуск 2381.

стандарт описывает обработку очень точно - это должно быть сделано "как будто" указанным фрагментом кода, который не принимает эти символы. Сравните разрешение LWG выпуск 221, в котором они добавили x и X в список персонажей потому что num_get как тогда-описано не будет иначе разбирать 0x для целочисленных входов.

Clang / libc++ принимает "inf" и "nan" вместе с hexfloats, но не "бесконечность" в качестве расширения. См.ошибка 19611.


в конце этапа 2, он говорит:

если он не отброшен, то выполняется проверка, чтобы определить, является ли c допускается в качестве следующего символа входного поля преобразования описатель возвращается на Этап 1. Если это так,то оно накапливается.

Если символ отбрасывается или накапливается, то in расширяется ++in и обработка возвращается к началу этапа 2.

Так что, возможно,a Не допускается %g спецификатор и он не накапливается или игнорируется.