为什么 cout.precision() 会影响整个流?

Why does cout.precision() affect the whole stream?

我觉得我问的是一个非常基本的问题,但我无法在这里或 Google 中找到答案。我记得我们在学校教过这个,但可惜它已经消失多年了。

为什么在输出列表中间调用cout.precision() (std::ios_base::precision())会影响整个流?我知道应该使用 std::setprecision() 来动态更改精度的规则,并且 cout.precision() 会破坏其值为 returns 的输出。但这是什么机制呢?是缓冲的原因吗?手册说明了这些 "do the same thing",但根据经验我可以看出这并不完全正确。

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

这是"implementation specific / not defined by the standard"吗? 请随意将其标记为重复,因为我无法在 SO 上找到它。

在您的第一个示例中计算 cout.precision(3) 的确切时间未定义,因为它的值用作函数调用的参数。 OTOH 对于第二个示例,setprecision(3) 插入流的时间定义非常明确 - 即在插入 endl 之后和 test 之前(第二次)。因此第一个版本不会产生可靠的结果(你得到的结果在不同的实现上可能会有所不同),但第二个版本会。

有关更详细的说明,请参阅 this question。 (那里的问题不使用运算符,但原理是相同的 - 在另一个函数的 return 值上调用成员函数。)

未指定函数参数评估的顺序。当您调用 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) 创建的临时对象会在任何时候创建。然后在调用适当的移位运算符时应用效果。由于必须以适当的顺序调用移位运算符,因此操纵器的使用发生在已知位置。