首页 > 解决方案 > 如何在自定义对象的 << 运算符中添加自定义前缀

问题描述

有没有办法在operator<<我实现的对象中添加自定义前缀?

前任:

class A {
   public:
    std::string id;
    int count;
};

std::ostream &operator<<(std::ostream &os, const A &a)
{
    os << os.prefix() << "Id: " << a.id << "\n";
    os << os.prefix() << "Count: " << a.count << "\n";
    return os;
}

如果我做这样的事情:

A a;
a.id = "foo";
a.count = 1;
std::cout << a << std::endl;

输出将是:

Id: foo
Count: 1

我想做类似的事情:

std::cout << set_prefix(" -") << a << std::endl;
std::cout << set_prefix("==>") << a << std::endl;

要获得这样的输出:

 -Id: foo
 -Count: 1
==>Id: foo
==>Count: 1

一个建议是使用std::setfilland os.fill,但std::setfill将单个char作为参数,我需要一个自定义字符串。

解决方案

查看operator<<(std::basic_ostream)文档,我发现了这个:

在插入之前,首先使用 扩展所有字符 os.widen(),然后按如下方式确定填充:如果要插入的字符数小于os.width(),则将足够的副本 os.fill()添加到字符序列中以使其长度相等os.width()。如果(out.flags()&std::ios_base::adjustfield) == std::ios_base::left,则将填充字符添加到输出序列的末尾,否则将它们添加到输出序列之前。插入后,width(0)调用取消 的效果( std::setw如果有)。

因此,对我有用的解决方案是在开始时保存流的原始宽度,而不是在必要时恢复它们。

std::ostream &operator<<(std::ostream &os, const A &a)
{
    auto w = os.width();
    os << std::setw(w) << "" << "Id: " << a.id << "\n";
    os << std::setw(w) << "" << "Count: " << a.count;
    return os;
}

然后:

std::cout << a << std::endl;
std::cout << std::setw(4) << a << std::endl;
std::cout << std::setfill('>') << std::setw(2) << a << std::endl;

给出以下输出:

Id: foo
Count: 1
    Id: foo
    Count: 1
>>Id: foo
>>Count: 1

标签: c++

解决方案


也许有点矫枉过正,但你可以使用这样的东西:

#include <iostream>
#include <sstream>

struct line_buffered_stream {
    std::ostream& out;
    std::stringstream ss;
    std::string prefix;
    line_buffered_stream(std::ostream& out,std::string prefix) : 
        out(out),prefix(prefix) {}        
    template <typename T> 
    auto operator<<(const T& t) -> decltype(this->ss << t,*this) { 
        ss << t; 
        return *this;
    }        
    ~line_buffered_stream(){
        std::string line;
        while (std::getline(ss,line)){
            out << prefix << line << "\n";
        }
    }
};

int main() {
     line_buffered_stream(std::cout,"==>") << "a\nb\n";
     line_buffered_stream(std::cout,"-->") << "a\nb\n";        
}

输出:

==>a
==>b
-->a
-->b

现场演示

请注意,上面的实现并不打算用作其他任何东西,只是一个临时的,其生命周期仅限于一行代码。如果您不喜欢这样,则必须添加一些机制来刷新流,std::cout以免等到调用析构函数。


推荐阅读