8

我正在研究ostream为某些数学类(矩阵、向量等)提供运算符。一位朋友注意到,ostream运算符的 gcc 标准库实现std::complex包括内部使用字符串流来格式化输出,然后将其传递给实际ostream

///  Insertion operator for complex values.
template<typename _Tp, typename _CharT, class _Traits>
  basic_ostream<_CharT, _Traits>&
  operator<<(basic_ostream<_CharT, _Traits>& __os, const complex<_Tp>& __x)
{
  basic_ostringstream<_CharT, _Traits> __s;
  __s.flags(__os.flags());
  __s.imbue(__os.getloc());
  __s.precision(__os.precision());
  __s << '(' << __x.real() << ',' << __x.imag() << ')';
  return __os << __s.str();
}

这种模式在 boost 中也可见。我们正在尝试确定这是否是值得遵循的模式。有人担心它涉及为字符串流包含一个额外的标头,并且在字符串流中需要额外的堆分配,这可能会被避免。

最合理的建议是,如果客户端需要该功能,那么他们可以创建字符串流并自己进行预传递。

谁能帮我理解为什么这会被认为是好的做法以及我是否应该采用它?

4

3 回答 3

6

考虑如果您在 ostream 上设置输出宽度,然后向其写入 std::complex 会发生什么 - 您不希望宽度仅影响第一个输出操作(即'('字符)

std::complex i(0, 1);
std::cout << std::setw(10) << std::left << i;

"(0,1)     "这不应该打印"(         0,1)"

通过将整个输出形成为单个字符串,然后将其写出,输出会尊重流上设置的字段宽度和其他格式标志。

于 2012-10-15T21:23:13.767 回答
4

另一个响应中引用的线程原因不会真正解决:字符串仍然可以在流缓冲区级别拆分,因为从多个线程调用时这些操作不是原子的。

但是,有两个相关的考虑因素:

  1. 对于某些输出,您希望临时更改格式标志设置。例如,您想确保某个字符串使用十六进制表示法出现,对于其他十进制表示法,并且您希望将流恢复到其原始状态。
  2. 更重要的是,输出的含义width()是整个格式化字符串至少应该占据的字符数。如果您在另一个输出运算符内部使用输出运算符,您将使第一个元素占据宽度,而不是由多个组件组成的整个字符串。例如,对于复数,实数元素将占据width()而不是实数元素、逗号和虚数元素的组合。
于 2012-10-15T21:26:42.540 回答
2

这种模式的一个主要目的是避免保留原始流的操纵器/标志并在返回之前重置它们。Boost.IoStateSavers不需要这样做,所以我会说使用所述库将是一个更好的做法。

于 2012-10-15T21:22:09.723 回答