在 C++ 中重写 << 运算符时如何正确遵守 std::setw 和 std::fill

How to properly honor std::setw and std::fill when overriding << operator in C++

我有一个表示字符串的自定义数据类型:

struct ByteArray {
   uint32_t len;
   uint8_t* ptr;
}

并覆盖 << 运算符以将其输出到屏幕。

版本 1:

ostream &operator<<(ostream &os, const ByteArray &dt) {
    os << string((const char*)dt.ptr,dt.len);
    return os;
}

版本 2:

ostream &operator<<(ostream &os, const ByteArray &dt) {
    os.write((const char *) dt.ptr, dt.len);
    auto fill = os.width() - dt.len;
    fill_n(std::ostream_iterator<char>(os), fill, os.fill());
    return os;
}

我的输出代码类似于

ByteArray& value = ...; // a string of length 25
cout << '|' << left << setw(40) << value;

当我使用版本 1 时,它运行良好并显示:

|gAaNbDxVTyFjjhgGodAKyy9uk               

但是版本 2 显示 17 个前导空白 spaces:

|                 gAaNbDxVTyFjjhgGodAKyy9uk    

我想使用版本 2,因为它不会创建不必要的字符串对象。但是我如何正确处理 setw 和 fill 呢?

谢谢!

更新:

当我输出多行时,它们是左对齐的,但在实际数据之前有前导 17 space。请看下面的例子。此输出由上面的版本 2 代码生成。当我设置宽度为40时,实际宽度为95。

|                 gAaNbDxVTyFjjhgGodAKyy9uk                                                      |
|                 wMEzrsX2KKpTaJGE3uGEUibymG                                                     |                                                   
|                 8cRzJOxCG7z qpfkXKgrQs6ubfOTK                                                  |                                               
|                 A5a1lovY,yQoSHaYon5cGgo1l                                                      |                                            
|                 f1mPa2ts2TUCbZ9UVmuDuu2lXLgfYTP                                                |           

代码中存在一些问题。

  1. 您应该在处理 width 时遵守 left 标志。 setw()width() 基本相同,后者通常根据 left 标志左对齐或右对齐。请参阅 operator << 以获取字符串:

    • If str.size() is not less than os.width(), uses the range [str.begin(), str.end()) as-is
    • Otherwise, if (os.flags() & ios_base::adjustfield) == ios_base::left, places os.width()-str.size(), copies of the os.fill() character after the character sequence
    • Otherwise, places os.width()-str.size() copies of the os.fill() character before the character sequence
  2. padding用一次就清空是很常见的:os.width(0);.

  3. ostream_iterator delegates to operator << 对于 char 类型。该运算符将依次尝试填充,这将导致一团糟。

    改用ostreambuf_iterator

像这样

ostream& operator<<(ostream& os, const ByteArray& dt) {
    size_t fill = os.width() > dt.len ? os.width() - dt.len : 0;
    if ((os.flags() & ios_base::adjustfield) == ios_base::left) {
        os.write((const char*)dt.ptr, dt.len);
        fill_n(std::ostreambuf_iterator<char>(os), fill, os.fill());
    }
    else {
        fill_n(std::ostreambuf_iterator<char>(os), fill, os.fill());
        os.write((const char*)dt.ptr, dt.len);
    }
    os.width(0);
    return os;
}

额外注意:测试填充时,谨慎的做法是在 之前打印一些内容。