Почему 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-й раз). Поэтому первая версия не даст надежных результатов (то, что вы получите, может отличаться в разных реализациях), но вторая версия даст.

Смотрите этот вопрос для более подробного объяснения. (Вопрос там не использует операторы, но принцип тот же - вызов функции-члена для возвращаемого значения другой функции.)

Другие вопросы по тегам