2011-05-18 30 views
5

全部,如何統一定義所有C++ IOStream機器人的插入算子?

爲什麼下面的代碼無法編譯爲'std :: endl',但對所有其他插入類型都適用?

#include <sstream> // ostringstream 

/// @brief A class that does streamed, formatted output via 'operator<<'. 
class My_Stream 
{ 
public: 
    /// @brief A member method that manipulates the underlying stream. 
    void foo() 
    { 
     m_oss << "foo_was_here; "; 
    } 

private: 
    /// @brief The underlying stream. 
    std::ostringstream m_oss; 

    /// @brief 'operator<<' is a friend. 
    template< typename T > 
    friend My_Stream& operator<<(My_Stream& a_r_my_stream, 
            const T& a_r_value); 
}; 

/// @brief A manipulator that calls a class method. 
My_Stream& manipulator_foo(My_Stream& a_r_my_stream) 
{ 
    a_r_my_stream.foo(); 
    return a_r_my_stream; 
} 

/// @brief The generic insertion operator. 
template< typename T > 
My_Stream& operator<<(My_Stream& a_r_my_stream, 
         const T& a_r_value) 
{ 
    a_r_my_stream.m_oss << a_r_value; 
    return a_r_my_stream; 
} 

/// @brief Define an iostream-like manipulator for my-stream. 
typedef My_Stream& (* my_stream_manipulator) (My_Stream&); 

/// @brief The specialized 'my_stream_manipulator' insertion operator. 
template<> 
My_Stream& operator<<(My_Stream& a_r_my_stream, 
         const my_stream_manipulator& a_r_manipulator) 
{ 
    return a_r_manipulator(a_r_my_stream); 
} 

int main(int argc, char* argv[]) 
{ 
    My_Stream my_stream; 

    my_stream << 'c'; // char 
    my_stream << "string"; // c-string 
    my_stream << 1u; // unsigned int 
    my_stream << -1; // signed int 
    my_stream << 5.3f; // float 
    my_stream << -23.345; // double 
    my_stream << std::boolalpha; // std::ios_base manipulator 
    my_stream << std::endl; // std::ostream manipulator 
    my_stream << manipulator_foo; // my_stream manipulator 

    return 0; 
} 

我碰到下面的G ++ 4.5的錯誤:

willo:~/test_cpp$ g++ -Wall test_overloaded_insertion_manipulators.cpp test_overloaded_insertion_manipulators.cpp: In function ‘int main(int, char**)’: test_overloaded_insertion_manipulators.cpp:60: error: no match for ‘operator<<’ in ‘my_stream << std::endl’

我期望的代碼實例化 '運營商< <' 的標準:: ENDL,就像它沒有爲基元,標準: :ios_base和我的自定義操縱器。

對於上下文,我試圖創建一個輕型API IOStream類的類與當前IOStream操縱器,以及一個或兩個更多的自定義操縱器。

+0

你確定它不會是更實用,只是從公開'ostringstream'繼承?您仍然可以添加自定義功能,但不需要費心去獲取stringstream本身已可以執行的操作。 – leftaroundabout 2011-05-18 23:21:29

+0

@leftaroundabout:大多數STL對象並不是真正被設計成可以從「std :: ostringstream」派生出來的,但似乎至少有一個虛擬析構函數,因此可以工作。 – AJG85 2011-05-18 23:40:53

+0

@leftaroundabout是的,我確定。這是一個玩具示例來調試我目前的問題。我想要的代碼必須攔截每個插入操作,並有條件地執行操作;所以我必須重新實施'運營商''對我的新班級。 – 2011-05-19 02:21:50

回答

7

因爲endl是一個函數模板:

template <class charT, class traits> 
basic_ostream<charT,traits>& endl(basic_ostream<charT,traits>& os); 

所以標識符本身不是一個值。它僅在實例化時變成一個值(函數指針)。但是你的operator<<本身就是一個模板,並且編譯器沒有類型信息來決定使用哪個類型實例化endl

相反,例如, boolalpha是:

ios_base& boolalpha(ios_base& str); 

因此,它爲什麼有效。

endl作品basic_ostream,因爲那一個限定operator<<重載爲採取函數指針成員函數;特別是:

basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>& (*pf)(basic_ostream<charT,traits>&)); 

所以像stream << endl呼叫時,它會知道從類型的thischarTtraits(即運營商的左側),這將賦予它確切類型函數指針的期望右側那麼它將用於實例化相應版本的endl。你可以爲你的班級做同樣的事情。

+0

就是這樣;我沒有把'endl'是模板函數這個事實連接起來,沒有一個函數。謝謝。 – 2011-05-19 02:23:18

2

您需要將流操縱器定義爲單獨的朋友。

添加這個朋友:

// Note: Untested (don't have a compiler here). 
template <class charT, class Traits> 
friend My_Stream& operator<<(My_Stream&, std::basic_ostream<charT, Traits>& (*)(std::basic_ostream<charT, Traits>&)); 

然後,你需要定義函數:

template <class charT, class Traits> 
My_Stream& operator<<(My_Stream& stream, std::basic_ostream<charT, Traits>& (*manip)(std::basic_ostream<charT, Traits>&)) 
{ 
    (*manip)(stream.m_oss); 
    return stream; 
} 
+0

這應該是模板嗎?由於'stream.m_oss'是一個特定的類型,因此只有'std :: ostream&(*)(std :: ostream&)'實際上可以在這裏工作。 – aschepler 2011-05-18 23:22:04

+0

該模板不適用於My_Stream,而是適用於std :: endl,這是我們所擔心的模板(這就是爲什麼我們首先遇到問題(因此您需要模板))。 – 2011-05-18 23:27:27

+0

+1這比我的擴展模板專業化要好,因爲它適用於可以定義的所有「std :: endl」模板類型。 – AJG85 2011-05-18 23:31:25

相關問題