2010-05-10 26 views
10

Estoy tratando de escribir mi propia clase de registro y utilizarlo como una corriente:corriente de C++ como un parámetro cuando la sobrecarga de operadores <<

logger L; 
L << "whatever" << std::endl; 

Este es el código Empecé con:

#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; 
} 

Pero yo estaba recibiendo un error al intentar compilar, diciendo que no había una definición para el operador < < (cuando se utiliza 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’ 

Por lo tanto, he intentado sobrecargar al operador < < para aceptar este tipo de transmisiones, pero me está volviendo loco. No sé cómo hacerlo. He estado buscando alojamiento en, por ejemplo, la definición de std :: endl en el archivo de cabecera ostream y escrito una función con esta cabecera:

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

Pero no hubo suerte. He intentado usar las mismas plantillas en lugar de usar directamente char, y también intenté simplemente usar "const ostream & os", y nada.

Otra cosa que me molesta es que, en la salida de error, el primer argumento a favor del operador < < cambios, a veces es una referencia a un puntero, a veces se parece a una doble referencia ...

+0

posible duplicado de [std :: endl es de tipo desconocido al sobrecargar al operador <<] (http://stackoverflow.com/questions/1134388/stdendl-is-of-unknown-type-when-overloading-operator) – sth

+1

Buena pregunta. Me encontré con este mismo problema la semana pasada. Triste voz de la experiencia en las respuestas a continuación ... –

+0

@ T.E.D .: Um ... gracias? –

Respuesta

9

endl es una bestia extraña. No es un valor constante. De hecho, es una función. Se necesita una anulación especial para manejar la aplicación de endl:

logger& operator<< (logger& log, ostream& (*pf) (ostream&)) 
{ 
    cout << pf; 
    return log; 
} 

Este acepta la inserción de una función que toma una referencia ostream y devuelve una referencia ostream. Eso es lo que endl es.

Edit: En respuesta a la interesante pregunta de FranticPedantic de "¿por qué el compilador no puede deducir esto automáticamente?". La razón es que si profundiza aún más, endl es en realidad una función de la plantilla . Se define como:

template <class charT, class traits> 
    basic_ostream<charT,traits>& endl (basic_ostream<charT,traits>& os); 

Es decir, puede tomar cualquier tipo de ostream como su entrada y salida. Entonces, el problema no es que el compilador no pueda deducir que T const & podría ser un puntero a la función, sino que no puede averiguar queendl quería pasar. La versión con plantilla de operator<< presentada en la pregunta aceptaría un puntero a cualquier función como su segundo argumento, pero al mismo tiempo, la plantilla endl representa un infinito conjunto de posibles funciones, por lo que el compilador no puede hacer nada significativo allí.

Proporcionar la sobrecarga especial de la operator<< cuyo argumento segunda coincide con una específica instanciación de la plantilla endlpermite la llamada a resolver.

+0

Gracias! Eso lo resolvió Ahora que veo su solución, encuentro que mi definición 'basic_ostream > & (* s) (basic_ostream > &)' que es una versión genérica de ostream, no funcionaba porque se definió "const". Una vez que dejé caer la const, funcionó. –

+0

Para mí, la pregunta real permanece, ¿por qué el compilador no puede deducir T como un puntero a la función y vincularse a él? –

+0

@FranticPedantic ¡Punto interesante! Editado en la respuesta. –

0

En C++ es el buffer de secuencia que encapsula el mecanismo de E/S subyacente. La secuencia en sí misma solo encapsula las conversiones en cadena y la dirección de E/S.

Por lo tanto, debe utilizar una de las clases de flujo predefinidas, en lugar de hacer la suya propia. Si tiene un nuevo destino al que desea que vaya su E/S (como un registro del sistema), lo que debe crear es su propio buffer de secuencia (derivado de std::streambuf).

5

endl es un manipulador IO, que es un funtor que acepta una secuencia por referencia, realiza alguna operación en ella y la devuelve, también por referencia. cout << endl es equivalente a cout << '\n' << flush, donde flush es un manipulador que vacía el búfer de salida.

En su clase, sólo tiene que escribir una sobrecarga para este operador:

logger& operator<<(logger&(*function)(logger&)) { 
    return function(*this); 
} 

Dónde logger&(*)(logger&) es el tipo de una función aceptar y devolver un logger por referencia. Para escribir sus propios manipuladores, acaba de escribir una función que coincide con la firma, y ​​tienen que realizar alguna operación en la corriente:

logger& newline(logger& L) { 
    return L << '\n'; 
} 
+0

Esto es más complicado que la respuesta aceptada, pero un enfoque interesante, no obstante. –

+0

@FranticPedantic: dos caras de la misma moneda, de verdad. –

Cuestiones relacionadas