Fluxo C ++ como um parâmetro ao sobrecarregar o operador <<
-
25-09-2019 - |
Pergunta
Estou tentando escrever minha própria aula de registro e usá -la como um fluxo:
logger L;
L << "whatever" << std::endl;
Este é o código com quem comecei:
#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;
}
Mas eu estava recebendo um erro ao tentar compilar, dizendo que não havia definição para o operador << (ao usar o 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’
Então, eu tenho tentado sobrecarregar o operador << para aceitar esse tipo de fluxo, mas está me deixando louco. Eu não sei como fazer isso. Eu tenho lotado, por exemplo, a definição de std :: endl no arquivo de cabeçalho do ostream e escrevi uma função com este cabeçalho:
logger& operator <<(logger& log, const basic_ostream<char,char_traits<char> >& (*s)(basic_ostream<char,char_traits<char> >&))
Mas sem sorte. Eu tentei o mesmo usando modelos em vez de usar diretamente, e também tentei simplesmente usar "Const Ostream & OS" e nada.
Outra coisa que me incomoda é que, na saída de erro, o primeiro argumento para o operador << muda, às vezes é uma referência a um ponteiro, às vezes parece uma referência dupla ...
Solução
endl
é uma besta estranha. Não é um valor constante. Na verdade, é, de todas as coisas, uma função. Você precisa de uma substituição especial para lidar com a aplicação de endl
:
logger& operator<< (logger& log, ostream& (*pf) (ostream&))
{
cout << pf;
return log;
}
Isso aceita a inserção de uma função que leva um ostream
referência e retorna um ostream
referência. Isso é o que endl
é.
Editar: Em resposta à questão interessante da Frantcedantentic ", por que o compilador não pode deduzir isso automaticamente?". A razão é que, se você se aprofundar ainda mais, endl
é realmente um modelo função. É definido como:
template <class charT, class traits>
basic_ostream<charT,traits>& endl ( basic_ostream<charT,traits>& os );
Isto é, pode levar qualquer tipo de ostream
como entrada e saída. Portanto, o problema não é que o compilador não possa deduzir isso T const &
pode ser um ponteiro de função, mas que não pode descobrir que endl
você pretendia passar. A versão modelada de operator<<
apresentado na pergunta aceitaria um ponteiro para qualquer função como seu segundo argumento, mas ao mesmo tempo, o endl
modelo representa um infinito Conjunto de funções em potencial, para que o compilador não possa fazer nada significativo lá.
Fornecendo a sobrecarga especial do operator<<
cujo segundo argumento corresponde a um específico Instanciação do endl
O modelo permite que a chamada seja resolvida.
Outras dicas
endl
é um manipulador de IO, que é um functor que aceita um fluxo por referência, executa alguma operação e retorna esse fluxo, também por referência. cout << endl
é equivalente a cout << '\n' << flush
, Onde flush
é um manipulador que libera o buffer de saída.
Na sua classe, você só precisa escrever uma sobrecarga para este operador:
logger& operator<<(logger&(*function)(logger&)) {
return function(*this);
}
Onde logger&(*)(logger&)
é o tipo de função aceitar e retornar um logger
por referência. Para escrever seus próprios manipuladores, basta escrever uma função que corresponda a essa assinatura e faça com que ela execute alguma operação no fluxo:
logger& newline(logger& L) {
return L << '\n';
}
Eu acredito que o problema é o seu fluxo não sobrecarregar operator<<
aceitar uma função que tenha o mesmo tipo que std::endl
Conforme ilustrado nesta resposta: std :: endl é do tipo desconhecido quando o operador de sobrecarga <
Em C ++ é o Buffer de fluxo que encapsula o mecanisim de E/S subjacente. O fluxo em si apenas encapsula as conversões em cordas e a direção de E/S.
Assim, você deve usar uma das classes de fluxo predefinidas, em vez de fazer as suas. Se você tem um novo alvo que deseja que seu E/S acesse (como um log do sistema), o que você deve criar é o seu Buffer de fluxo (derivado de std::streambuf
).