Я пытаюсь записать свой собственный класс входа и использовать его в качестве потока:
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;
}
Но я получал ошибку при попытке скомпилировать, высказывании, что не было никакого определения для оператора <<(при использовании станд.:: 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’
Так, я пытался перегрузить оператор <<для принятия этого вида потоков, но он сводит меня с ума. Я не знаю, как сделать это. Я смотрел на, например, определение станд.:: endl в ostream заголовочном файле и записанный функция с этим заголовком:
logger& operator <<(logger& log, const basic_ostream<char,char_traits<char> >& (*s)(basic_ostream<char,char_traits<char> >&))
Но никакая удача. Я попробовал те же шаблоны использования вместо того, чтобы непосредственно использовать символ и также попытался просто использовать "константу ostream& OS" и ничто.
Другая вещь, которая ошибки меня - то, что, в выводе ошибок, первом аргументе в пользу оператора <<изменения, иногда это - ссылка на указатель, иногда похожа на двойную ссылку...
endl
- странный зверь. Это не постоянное значение. На самом деле это функция. Вам нужно специальное переопределение для обработки применения endl
:
logger& operator<< (logger& log, ostream& (*pf) (ostream&))
{
cout << pf;
return log;
}
Это позволяет вставить функцию, которая принимает ссылку на ostream
и возвращает ссылку на ostream
. Вот что такое endl
.
Edit: В ответ на интересный вопрос FranticPedantic "почему компилятор не может вывести это автоматически?". Причина в том, что если копнуть еще глубже, то endl
на самом деле сама по себе является шаблонной функцией. Она определена так:
template <class charT, class traits>
basic_ostream<charT,traits>& endl ( basic_ostream<charT,traits>& os );
То есть, она может принимать любой поток
в качестве входа и выхода. Проблема не в том, что компилятор не может понять, что T const &
может быть указателем функции, а в том, что он не может понять, какой endl
вы хотели передать. Шаблонная версия operator<<
, представленная в вопросе, примет указатель на любую функцию в качестве второго аргумента, но в то же время шаблон endl
представляет бесконечное множество потенциальных функций, поэтому компилятор не может сделать там ничего осмысленного.
Предоставление специальной перегрузки operator<<
, второй аргумент которой соответствует конкретному экземпляру шаблона endl
, позволяет разрешить вызов.
Я считаю, что проблема в том, что ваш поток не перегружает operator <<
, чтобы принять функцию того же типа, что и std :: endl
, как показано в этом ответе: std :: endl имеет неизвестный тип при перегрузке оператора <<
В C ++ именно буфер потока инкапсулирует базовый механизм ввода-вывода. Сам поток инкапсулирует только преобразования в строку и направление ввода-вывода.
Таким образом, вам следует использовать один из предопределенных потоковых классов, а не создавать свои собственные. Если у вас есть новая цель, к которой вы хотите перейти (например, системный журнал), то вам следует создать собственный буфер потока (полученный из std :: streambuf
]).
endl
- это манипулятор ввода-вывода, который представляет собой функтор, который принимает поток по ссылке, выполняет над ним некоторую операцию и возвращает этот поток, также по ссылке. cout << endl
эквивалентно cout << '\ n' << flush
, где flush
- это манипулятор, очищающий выходной буфер.
В вашем классе вам просто нужно написать перегрузку для этого оператора:
logger& operator<<(logger&(*function)(logger&)) {
return function(*this);
}
Где logger & (*) (logger &)
- это тип функции, принимающей и возвращающей logger
по ссылке. Чтобы написать свои собственные манипуляторы, просто напишите функцию, которая соответствует этой сигнатуре, и попросите ее выполнить некоторую операцию с потоком:
logger& newline(logger& L) {
return L << '\n';
}