Образец кода в Колиру:
#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). В [istream.formatted.arithmetic] мы имеем (перефразировано):
operator>>(double& val);
Как и в случае со вставками, эти экстракторы зависят от 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);
Глядя на 22.4.2.1:
Детали этой операции происходят в три этапа
— Этап 1: определить спецификатор конверсии
— Стадия 2: извлечение символов из in и определение соответствующего значения символа для формата
ожидается согласно спецификации преобразования, определенной на этапе 1.
— Этап 3: результаты магазина
В описании Стадии 2 я слишком долго вставляю все это сюда. Однако в нем четко сказано, что все символы должны быть извлечены до попытки преобразования; и далее, что именно следующие символы должны быть извлечены:
0123456789abcdefxABCDEFX+-
decimal_point()
thousands_sep()
Наконец, правила для Стадии 3 включают в себя:
— Для значения с плавающей запятой функция
strtold
,Числовое значение, которое будет сохранено, может быть одним из:
— ноль, если функция преобразования не может преобразовать все поле.
Кажется, все это четко указывает на то, что вывод моего кода должен быть 0, 'ipxngh'
, Тем не менее, это на самом деле выводит что-то еще.
Это ошибка компилятора / библиотеки? Есть ли какое-то положение, которое я пропускаю, чтобы локаль изменила поведение этапа 2? (В Другой вопрос кто-то опубликовал пример системы, которая на самом деле извлекает символы, но также извлекает ipxn
которых нет в списке, указанном в N3337).
Как указывает Perreal, этот текст из Стадии 2 актуален:
Если сброс — это правда, то, если ’.’ Еще не накоплено, то положение персонажа
запоминается, но персонаж игнорируется. В противном случае, если «уже было
накапливается, персонаж отбрасывается, и этап 2 заканчивается. Если это не отбрасывается, то
выполняется проверка, чтобы определить,c
допускается в качестве следующего символа поля ввода спецификатора преобразования, возвращаемого этапом 1. Если это так, он накапливается.Если символ либо отбрасывается, либо накапливается, то в него добавляется ++ in и выполняется обработка
возвращается к началу этапа 2.
Таким образом, этап 2 может завершиться, если символ находится в списке разрешенных символов, но не является допустимым символом для %g
, Это не говорит точно, но, вероятно, это относится к определению fscanf
от C99, что позволяет:
- непустая последовательность десятичных цифр, необязательно содержащая десятичную точку
символ, затем необязательная часть экспоненты, как определено в 6.4.4.2;- 0x или 0X, то непустая последовательность шестнадцатеричных цифр, необязательно содержащая
символ десятичной точки, затем необязательная двоичная экспоненциальная часть, как определено в 6.4.4.2;- INF или INFINITY, игнорируя регистр
- NAN или NAN (опция n-char-sequence opt), игнорируя регистр в части NAN, где:
а также
В отличие от локали «C», могут быть приняты дополнительные формы предметной последовательности, специфичные для локали.
Итак, на самом деле вывод Coliru правильный; и на самом деле обработка должен попытаться проверить последовательность символов, извлеченных до допустимого ввода в %g
, извлекая каждый символ.
Следующий вопрос: разрешено ли, как в ветке, на которую я ссылался ранее, принять i
, n
, p
и т.д. на этапе 2?
Это допустимые символы для %g
однако их нет в списке атомов, которые Стадии 2 разрешено читать (т.е. c == 0
за мою последнюю цитату, поэтому персонаж не сбрасывается и не накапливается).
Это беспорядок, потому что вполне вероятно, что ни реализация gcc / libstdc ++, ни реализация clang / libc ++ не соответствуют. Непонятно, «выполняется проверка, чтобы определить, разрешен ли c как следующий символ поля ввода спецификатора преобразования, возвращаемого на этапе 1», но я думаю, что использование фразы «следующий символ» означает, что проверка должна быть контекстно-зависимый (т.е. зависящий от уже накопленных символов), и, таким образом, попытка разобрать, например, "21abc"
, должен остановиться, когда 'a'
встречается. Это согласуется с обсуждением в Выпуск LWG 2041, который добавил это предложение обратно в стандарт после того, как он был удален во время разработки C ++ 11. отказ libc ++ сделать это ошибка 17782.
libstdc ++, с другой стороны, отказывается анализировать "0xABp-4"
мимо 0
, который на самом деле явно не соответствует на основе стандарта (он должен анализировать "0xAB"
в виде шестигранника, как четко разрешено C99 fscanf
спецификация для %g
).
Принятие i
, p
, а также n
не допускается стандартом. Увидеть Выпуск LWG 2381.
Стандарт описывает обработку очень точно — она должна выполняться «как если бы» указанным фрагментом кода, который не принимает эти символы. Сравните разрешение LWG выпуск 221, в котором они добавили x
а также X
в список символов, потому что num_get
как тогда описано не будет анализировать 0x
для целочисленных входов.
Clang / libc ++ принимает «inf» и «nan» вместе с hexfloats, но не «infinity» как расширение. Увидеть ошибка 19611.
В конце этапа 2 говорится:
Если он не отбрасывается, выполняется проверка, чтобы определить, является ли c
разрешено в качестве следующего символа входного поля преобразования
спецификатор, возвращаемый на этапе 1. Если это так, он накапливается.Если символ либо отбрасывается, либо накапливается, то значение in повышается с помощью ++ in, и обработка возвращается к началу этапа 2.
Так что возможно a
не допускается в %g
спецификатор и он не накапливается или игнорируется.