Почему cout.precision() влияет на весь поток?
Я чувствую, что задаю очень простой вопрос, но я не смог найти ответ здесь или в Google. Я помню, что нас этому учили в школе, но, увы, за многие годы это исчезло.
Почему cout.precision()
(std::ios_base::precision()
) влияет на весь поток при вызове в середине списка вывода? Я знаю правило, что std::setprecision()
следует использовать для изменения точности на лету, и это cout.precision()
собирается испортить вывод значением, которое он возвращает. Но какова механика этого? Это из-за буферизации? В руководствах говорится, что они "делают то же самое", но эмпирически я вижу, что это не совсем так.
MCVE:
const double test = 1.2345;
cout.setf(ios::fixed);
cout.setf(ios::showpoint);
cout.precision(2);
cout << test << endl << cout.precision(3) << test << endl;
// Output:
// 1.234
// 21.234
// after the same init
cout.precision(2);
cout << test << endl << setprecision(3) << test << endl;
// Output
// 1.23
// 1.234
Является ли это "реализацией конкретной / не определенной стандартом"? Не стесняйтесь пометить это как дубликат, потому что я не смог найти его на SO.
2 ответа
Порядок оценки аргументов функции не указан. Когда вы звоните std::cout.precision(n)
точность std::cout
'устанавливается в момент, когда этот вызов оценивается. В вашем выражении
cout << test << endl << cout.precision(3) << test << endl;
cout.precision(3)
это, по-видимому, называется первым, что полностью разрешено компилятору. Помните, что компилятор считает приведенное выше выражение эквивалентным
std::cout.operator<<(test)
.operator<<(std::endl)
.operator<<(std::cout.preision(3))
.operator<<(test)
.operator<< (std::endl);
Практически кажется, что аргументы вашей функции компилятора вычисляются справа налево. Только тогда выполняются различные операторы сдвига. В результате точность изменяется до выполнения вывода.
Использование манипуляторов, таких как std::setprecision(n)
избегает полагаться на порядок, подвыражения оцениваются: временный созданный из std::setprecision(n)
создается всякий раз, когда это. Затем эффект применяется, когда вызывается соответствующий оператор сдвига. Поскольку операторы сдвига должны вызываться в соответствующем порядке, использование манипулятора происходит в известном месте.
Точное время, когда cout.precision(3)
оценивается в вашем первом примере не определено, потому что его значение служит аргументом для вызова функции. OTOH со вторым примером, время, когда setprecision(3)
вставляется в поток очень хорошо определено - т.е. после endl
вставляется и перед test
(2-й раз). Поэтому первая версия не даст надежных результатов (то, что вы получите, может отличаться в разных реализациях), но вторая версия даст.
Смотрите этот вопрос для более подробного объяснения. (Вопрос там не использует операторы, но принцип тот же - вызов функции-члена для возвращаемого значения другой функции.)