2015-10-05 110 views
5

我知道这是可能超载基本类型(流)的运营商如下:全球格式化选项

std::ostream & operator<<(std::ostream &stream, const double s) { 
    stream << std::scientific << std::showpos << std::setprecision(15) << std::to_string(s); 
    return stream; 
} 

什么是用于定义原始类型的全局格式化选项的首选方式?请注意,我想将格式应用于任何类型的输出流,而不仅仅是像std::cout这样的特定流。欢迎使用C++ 11解决方案。任何提示?

+2

您可以定义一个应用所需选项的操纵器。显式是好的,隐含的是不好的。大致。 –

+0

即使可能,您也不应该(想要)那样做。这会让你的代码更难理解和维护。 – Walter

+0

@ Cheersandhth.-Alf你能更明确吗? – JorenHeit

回答

2

重载流是一个坏主意,但你可以包装它们。如果你想做一个特殊的但通用的前/后处理,定义一个自定义(模板)类有一个流作为成员(可能在构造函数中影响),并委托实际的io到预处理输入和/或做一些后处理。

class Printer { 
    ostream &ost; 

    Printer(ostream& st): ost(st) { 
     // other initializations ... 
    } 

    Printer& operator << (double d) { 
     ost << std::scientific << std::showpos << std::setprecision(15) << std::to_string(s); 
     return *this; 
    } 
    // others: ostream conversion, other processing... 
} 
+0

你应该指定为什么*重载流是一个坏主意*。 – Walter

+1

@Walter:因为尽可能多的标准库类,它们没有被设计为重载(没有虚拟析构函数,没有办法让标准流实现你的自定义类) –

+0

伟大的建议! – Marcel

5

您可以定义自己的操纵器来设置流格式器。你的手必须符合由<<运营商所需要的格式:

basic_ostream& operator<<(
    std::basic_ostream<CharT,Traits>& (*func)(std::basic_ostream<CharT,Traits>&)); 

因此,例如:

template <class Char> 
basic_ostream<Char>& format_double(basic_ostream<Char>& stream) { 
    return stream << std::scientific 
        << std::showpos 
        << std::setprecision(15); 
} 

然后,只需做:

cout << format_double << 2.0; 

参考

有两种格式操纵器,一个取ios_base作为参数,另一个是basic_ostream。因为没有指定setprecision(int)的返回值是什么,所以您必须使用后者。例如,我的编译器(gcc-4.9)返回一个结构体_Setprecision,它包含整数并为此结构体定义了一个特定的运算符<<,并为此定义了basic_ostream

+0

正是我刚刚说的! +1 – Walter

+0

好的建议,但我敢打赌,至少在代码的某些地方(如果你在一个团队中工作,这尤其如此),并且最终输出的格式不同。在某些应用程序中,最好总是以相同的方式格式化浮点数。 – Marcel

0

要在fjardon's answer扩大,您可以创建任何类型的默认格式,您希望使用的模板,这样

cout << formatted(2.0) << ' ' << formatted(1.4,6) << ' ' << formatted(2.3f) 

使用不同的精度等。这可以通过

namespace io_formatting { 
    template<typename Tp> 
    struct manipulator 
    { 
    Tp val; 
    int prec; 
    manipulator(Tp x, int p=std::numeric_limits<Tp>::digits10) 
    : val(x), prec(p) {}   
    }; 
    template<typename X> 
    inline std::ostream& operator<<(std::ostream&o, manipulator<X> const&m) 
    { 
    return o << std::scientific 
      << std::showpos 
      << std::setprecision(m.prec) 
      << m.val; 
    } 
} 
template<typename Tp> 
io_formatting::manipulator<Tp> formatted(Tp x) { return {x}; } 
template<typename Tp> 
io_formatting::manipulator<Tp> formatted(Tp x, int p) { return {x,p}; } 
实施

您可以使用专业化和/或SFINAE来区分不同类型(浮点,积分,复杂...)。

+0

这与fjardon的完全不同。这跟随着“漂亮打印机”的方法比机械手概念更多。 – BeyelerStudios

2

可以通过确保数据流使用不同的std::locale来修改数值类型(整数和浮点数)的格式。该设施可以使用来影响在之后创建的所有流的格式化全局std::locale被设置。现有的流也可以使用这种修改的std::localeimbue() ing。

一个例子可能是这个样子:

class special_num_put 
    : public std::num_put<char> { 
    virtual iter_type do_put(iter_type it, ios_base& fmt, 
          char_type fill, double v) const { 
     fmt.precision(15); 
     fmt.setf(std::ios_base::showpos); 
     fmt.setf(std::ios_base::scientific, std::ios_base::floatfield); 
     return this->std::num_put<char>::do_put(it, fmt, fill, v); 
    } 
    // likewise for all of the other numeric types 
}; 

int main() { 
    std::locale::global(std::locale(std::locale(), new special_num_put)); 
    std::cout.imbue(std::locale()); // deal with existing stream 
    // likewise for all other preexisting streams 
} 

注意,我与早期,你应该想要做这样的事情作出的评论表示赞同。