Поток С++ в качестве параметра при перегрузке оператора <<
Я пытаюсь написать свой собственный класс ведения журнала и использовать его как поток:
logger L;
L << "whatever" << std::endl;
Это код, который я начал с:
#include <iostream>
using namespace std;
class logger{
public:
template <typename T>
friend logger& operator <<(logger& log, const T& value);
};
template <typename T>
logger& operator <<(logger& log, T const & value) {
// Here I'd output the values to a file and stdout, etc.
cout << value;
return log;
}
int main(int argc, char *argv[])
{
logger L;
L << "hello" << '\n' ; // This works
L << "bye" << "alo" << endl; // This doesn't work
return 0;
}
Но я получал ошибку при попытке скомпилировать, говоря, что не было определения для оператора < < (при использовании std:: endl):
pruebaLog.cpp:31: error: no match for ‘operator<<’ in ‘operator<< [with T = char [4]](((logger&)((logger*)operator<< [with T = char [4]](((logger&)(& L)), ((const char (&)[4])"bye")))), ((const char (&)[4])"alo")) << std::endl’
Итак, я пытался перегрузить оператор < < принять подобные потоки, но это сводит меня с ума. Я не знаю, как это сделать. Я, например, рассматривал определение std:: endl в заголовочном файле ostream и записывал функцию с этим заголовком:
logger& operator <<(logger& log, const basic_ostream<char,char_traits<char> >& (*s)(basic_ostream<char,char_traits<char> >&))
Но не повезло. Я пробовал использовать шаблоны вместо прямого использования char, а также попробовал просто использовать "const ostream & os" и ничего.
Еще одна вещь, которая меня беспокоит, заключается в том, что в выводе ошибки первый аргумент для оператора < < изменения, иногда это ссылка на указатель, иногда выглядит как двойная ссылка...
Ответы
Ответ 1
endl
- странный зверь. Это не постоянное значение. Это на самом деле, из всех вещей, функция. Для обработки приложения endl
требуется специальное переопределение:
logger& operator<< (logger& log, ostream& (*pf) (ostream&))
{
cout << pf;
return log;
}
Это принимает вставку функции, которая принимает ссылку ostream
и возвращает ссылку ostream
. Это endl
.
Изменить: В ответ на интересный вопрос FranticPedantic о том, почему компилятор не может это сделать автоматически? Причина в том, что если вы еще глубже погружаетесь, endl
фактически является функцией шаблона. Он определяется как:
template <class charT, class traits>
basic_ostream<charT,traits>& endl ( basic_ostream<charT,traits>& os );
То есть, он может принимать любой тип ostream
в качестве своего ввода и вывода. Поэтому проблема заключается не в том, что компилятор не может определить, что T const &
может быть указателем на функцию, но он не может определить, к какому endl
вы должны были перейти. Шаблонная версия operator<<
, представленная в вопрос будет принимать указатель на любую функцию в качестве второго аргумента, но в то же время шаблон endl
представляет бесконечный набор потенциальных функций, поэтому компилятор не может там ничего значимого.
Предоставление специальной перегрузки operator<<
, второй аргумент которой соответствует конкретному экземпляру шаблона endl
, позволяет разрешить вызов.
Ответ 2
endl
- это манипулятор IO, который является функтором, который принимает поток по ссылке, выполняет некоторую операцию над ним и возвращает этот поток также по ссылке. cout << endl
эквивалентен cout << '\n' << flush
, где flush
- это манипулятор, который сбрасывает выходной буфер.
В вашем классе вам просто нужно написать перегрузку для этого оператора:
logger& operator<<(logger&(*function)(logger&)) {
return function(*this);
}
Где logger&(*)(logger&)
- это тип функции, принимающей и возвращающий logger
по ссылке. Чтобы написать свои собственные манипуляторы, просто напишите функцию, которая соответствует этой сигнатуре, и попросите ее выполнить некоторую операцию в потоке:
logger& newline(logger& L) {
return L << '\n';
}
Ответ 3
В С++ это буфер потока, который инкапсулирует базовый механизм ввода-вывода. Сам поток только инкапсулирует преобразования в строку и направление ввода/вывода.
Таким образом, вы должны использовать один из предопределенных классов потоков, а не создавать свои собственные. Если у вас есть новая цель, к которой вы хотите, чтобы ваш ввод-вывод выполнялся (например, системный журнал), то вы должны создавать собственный буфер потока (полученный от std::streambuf
).
Ответ 4
Я считаю, что проблема в том, что ваш поток не перегружает operator<<
, чтобы принять функцию, которая имеет тот же тип, что и std::endl
, как показано в этом ответе: std:: endl неизвестного типа, когда оператор перегрузки < <