Поток C ++ в качестве параметра при перегрузке оператора << - PullRequest
10 голосов
/ 10 мая 2010

Я пытаюсь написать свой собственный класс ведения журнала и использовать его в качестве потока:

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): </p>

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 и написал функцию с таким заголовком: </p>

logger& operator <<(logger& log, const basic_ostream<char,char_traits<char> >& (*s)(basic_ostream<char,char_traits<char> >&))

Но не повезло. Я попробовал то же самое, используя шаблоны вместо прямого использования char, а также попробовал просто использовать "const ostream & os" и ничего.

Еще одна вещь, которая меня беспокоит, это то, что в выводе ошибки изменяется первый аргумент оператора <<, иногда это ссылка на указатель, иногда выглядит как двойная ссылка ... </p>

Ответы [ 4 ]

9 голосов
/ 10 мая 2010

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, позволяющему разрешить вызов.

5 голосов
/ 10 мая 2010

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';
}
0 голосов
/ 10 мая 2010

Я считаю, что проблема в том, что ваш поток не перегружается operator<<, чтобы принять функцию того же типа, что и std::endl, как показано в этом ответе: std :: endl имеет неизвестный тип при перегрузке оператор << </a>

0 голосов
/ 10 мая 2010

В C ++ это потоковый буфер , который инкапсулирует базовый механизм ввода / вывода. Сам поток инкапсулирует только преобразования в строку и направление ввода / вывода.

Таким образом, вы должны использовать один из предопределенных потоковых классов, а не создавать свой собственный. Если у вас есть новая цель, которую вы хотите использовать для ввода-вывода (например, системный журнал), вам нужно создать собственный потоковый буфер (полученный из std::streambuf).

...