Pregunta

Estoy intentando escribir mi propia clase de registro y usarla como una secuencia:

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

Este es el código con el que comencé:

#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 recibí un error al intentar compilar, diciendo que no había ninguna definición para operador<< (al usar 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’

Entonces, he estado tratando de sobrecargar operator<< para aceptar este tipo de transmisiones, pero me está volviendo loco.No sé cómo hacerlo.He estado mirando, por ejemplo, la definición de std::endl en el archivo de encabezado ostream y escribí una función con este encabezado:

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

Pero no hubo suerte.Intenté lo mismo usando plantillas en lugar de usar char directamente, y también intenté simplemente usar "const ostream& os", y nada.

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

¿Fue útil?

Solución

endl es una bestia extraña. No es un valor constante. En realidad, de todas las cosas, 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 de ostream. Eso es lo que es endl.

Editar En respuesta a la interesante cuestión de FranticPedantic de "¿por qué no el compilador deduce esto automáticamente?". La razón es que si aún más profundo cave, endl en realidad es en sí mismo un plantilla función. 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. Así que el problema no es que el compilador no puede deducir que T const & podría ser un puntero de función, pero que no puede averiguar , que endl que significaba para pasar. La versión con plantilla de operator<< presentó en la pregunta sería aceptar un puntero a una función como segundo argumento, pero al mismo tiempo, la plantilla endl representa un infinito conjunto de funciones potenciales, por lo que el compilador no puede hacer nada significativo allí.

Proporcionar la sobrecarga especial de la operator<< cuyo argumento segunda coincide con un específica instanciación de la plantilla endl permite que la llamada resolución.

Otros consejos

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

En tu clase, sólo necesitas escribir una sobrecarga para este operador:

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

Dónde logger&(*)(logger&) es el tipo de función que acepta y devuelve un logger por referencia.Para escribir sus propios manipuladores, simplemente escriba una función que coincida con esa firma y haga que realice alguna operación en la secuencia:

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

Creo que el problema es que su flujo no sobrecarga operator<< para aceptar una función que tiene el mismo tipo que std::endl como se ilustra en esta respuesta: std :: endl es de tipo desconocido cuando la sobrecarga de operadores <<

En C ++ es el corriente tampón que encapsula el subyacente I / O mechanisim. La corriente en sí sólo encapsula las conversiones a la secuencia, y la dirección de E / S.

Por lo tanto usted debe utilizar una de las clases de flujo predefinidos, en lugar de hacer su propio. Si usted tiene un nuevo destino que desea su I / O para ir a (como un registro del sistema), lo que debe ser la creación de su propio es flujo de búfer (derivado de std::streambuf).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top