Как определить операторы вставки для всех манипуляторов COS IOStream? - PullRequest
6 голосов
/ 19 мая 2011

All

Почему следующий код не компилируется для '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: в функции int int (int, char **): test_overloaded_insertion_manipulators.cpp: 60: ошибка: нет совпадения для оператора "<<" в "my_stream << std :: endl" </p>

Я ожидаю, что код создаст экземпляр 'operator <<' для std :: endl, как это было сделано для примитивов, std :: ios_base и моего пользовательского манипулятора. </p>

Для контекста я пытаюсь создать IOStream-подобный класс с легким API, который работает с текущими манипуляторами IOStream, а также с одним или двумя пользовательскими манипуляторами.

Ответы [ 2 ]

7 голосов
/ 19 мая 2011

Поскольку endl является шаблоном функции:

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

Таким образом, сам идентификатор не является значением.Он становится значением (указателем на функцию), только когда создается его экземпляр.Но ваш operator<< сам по себе является шаблоном, и нет никакой информации о типах, доступной для компилятора, чтобы решить, с какими типами создавать экземпляр endl.1011 *

Следовательно, почему это работает.

endl работает для basic_ostream, потому что тот определяет перегрузки operator<< как функции-члены, принимающие указатели на функции;в частности:

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

Таким образом, при вызове, подобном stream << endl, он будет знать charT и traits из типа this (то есть с левой стороны оператора), и это даст емуточный тип указателя на функцию, ожидаемый с правой стороны - который он затем использовал бы для создания экземпляра соответствующей версии endl.Вы можете сделать то же самое для своего класса.

2 голосов
/ 19 мая 2011

Вам необходимо определить потоковые манипуляторы как отдельного друга.

Добавить этого друга:

// 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;
}
...