如何处理自定义输出运算符中的iomanips?

时间:2018-08-08 15:15:14

标签: c++ iomanip

我刚刚遇到了将自定义输出运算符与io-manipulators相结合的问题。也许我的期望完全没有实现,但是如果

std::cout << foo() << "\n";

打印

00

那我期望

std::cout << std::left << std::setw(20) << foo() << "!\n"

打印

00                   !

但已实现

#include <iostream>
#include <iomanip>

struct foo { int a,b; };
std::ostream& operator<<(std::ostream& out, const foo& f) {
    out << f.a << f.b;
    return out;
}

int main() {
    std::cout << foo() << "\n";
    std::cout << std::left << std::setw(20) <<  foo() << "!";
}

屏幕上显示的内容是

00
0                   0!

我基本上看到两种选择:A)我的期望是错误的。 B)我改用此实现:

std::ostream& operator<<(std::ostream& out, const foo& f) {
    std::stringstream ss;
    ss << f.a << f.b;
    out << ss.str();
    return out;
}

但是,考虑到大多数情况下不使用io机械手,这似乎有些开销。

在自定义输出运算符中“正确”对待io操作器的惯用方式是什么?

2 个答案:

答案 0 :(得分:2)

恐怕没有简单的答案。如果您只需要处理std::setwstd::left,则解决方案是惯用的,但是对于其他操作,您必须确定格式化程序的行为。

例如,想象一下,如果您的结构具有 floats 而不是ints:

struct foo { float a,b; };

然后,您的用户尝试执行此操作:

const long double pi = std::acos(-1.L);
std::cout << std::setprecision(10) << foo{0.0f, pi} << "!\n"

这是您必须决定的时间:是要尊重输出流的precision属性,还是要忽略它?您当前的实现会忽略它,因为它会在另一个流中进行实际的转换。

要使用precision属性,您必须将其复制:

std::ostream& operator<<(std::ostream& out, const foo& f) {
    std::stringstream ss;
    ss.precision(out.precision());
    ss << f.a << f.b;
    out << ss.str();
    return out;
}

对于整数,还必须考虑是否愿意兑现std::setbase

对于其他操纵器,例如std::setfill,必须采用相同的推理。

答案 1 :(得分:1)

不一定惯用,但一种可能的解决方法是:

struct foo { 
    int a,b;
    std::string toString() {
        std::stringstream ss;
        ss << a << b;
        return ss.str();
    }
};

std::ostream& operator<<(std::ostream& out, const foo& f) {
    out << a << b;
    return out;
};

现在,调用者可以选择输出是否应为整体:

std::cout << std::left << std::setw(20) << foo().toString() << "!"; // output as expected
std::cout << foo();                                                 // output as expected 
                                                                    // and no unnecessary overhead

也许还会有人争辩说输出已经很慢了,所以一点额外的开销都不会造成伤害,只需根据stringify方法简单地实现输出运算符即可:

std::ostream& operator<<(std::ostream& out, const foo& f) {
    out << f.toString();
    return out;
}

这也解决了第一种方法的小缺点,该方法基本上用几乎相同的代码两次实现了相同的事情。