Как определить операторы вставки для всех манипуляторов COS 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: в функции 'int main(int, char**)': test_overloaded_insertion_manipulators.cpp:60: ошибка: нет соответствия для "оператора<<" в "my_stream <"
Я ожидаю, что код создаст экземпляр 'operator<<' для std:: endl, как это было сделано для примитивов, std:: ios_base и моего пользовательского манипулятора.
Для контекста я пытаюсь создать IOStream-подобный класс с легким API, который работает с текущими манипуляторами IOStream, а также с одним или двумя пользовательскими манипуляторами.
2 ответа
Так как 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
было бы знать charT
а также traits
от типа this
(т.е. слева от оператора), и это даст ему точный тип указателя на функцию, ожидаемый с правой стороны - который он затем будет использовать для создания экземпляра соответствующей версии endl
, Вы можете сделать то же самое для своего класса.
Вам нужно определить потоковые манипуляторы как отдельного друга.
Добавить этого друга:
// 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;
}