std::setw() 和 ostream::width() 之间的类型不一致

type inconsistency between std::setw() and ostream::width()

这个简单的代码

template<typename T>
std::ostream&operator<<(std::ostream&s, some_array_type<T> const&x)
{
  auto w = s.width();
  auto p = s.precision();
  s << x[0];
  for(std::size_t i=1; i!=x.size(); ++i)
    s << ' ' << std::setw(w) << std::setprecision(p) << m[i];
  return s;
}

打算打印一个 some_array_type 每个元素的宽度和精度等于当前值,允许像

这样的代码
some_array_type<double> x;
std::cout << std::setw(12) << std::setprecision(8) << x << std::endl;

然而,正如 clang 指出的那样,ostream::width()ostream::precision() (std::size_t) 返回的类型不同于操纵器 std::setwstd::setprecision (int),这样上面的代码会触发两个警告。

这种不一致是否有特殊原因,或者这只是 C++ 标准中的一个小缺陷(或 libc++ 实现的错误)?

首先,这显然是实施中的一个错误。标准说 std::ios_base::widthstd::ios_base::precision 使用 std::streamsize,要求是“signed basic” 整数类型”——在现代系统中,我希望 long long,或者可能 longstd::size_t要求无符号, 并且可以说也不是 "basic integral type" (尽管它可能是 一个类型定义)。

事实仍然是成员函数 std::ios_base::widthstd::ios_base::precision 可能(并且很可能会)使用不同的类型 比操纵器(总是 int)。而如果 std::streamsizelong long,它的某些值不会 适合 int。这样一个值实际出现的概率 正确的代码对我来说似乎很小,我会坚持下去 int(而不是混淆 auto),并且不用担心风险 溢出。或者,我会使用 int,但之前使用 assert,以 确保没有溢出。

最后:通常情况下,宽度是元素的总宽度。所以 您应该设置的宽度是 w / x.size() - 1(包括 第一个元素)。至少在理论上;我不确定这对 数组类型(我当然不会坚持,只要 修改后的语义有据可查)。当然,精度是 黏;您不必为每个值都设置它。 (另一方面, 用户应该记住并恢复它。)