2015-10-05 138 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 
} 

注意,我與早期,你應該想要做這樣的事情作出的評論表示贊同。