Pregunta

Considere el siguiente ejemplo mínimo:

#include <iostream>

using namespace std;

class myostream : public ostream {
    public:
        myostream(ostream const &other) :
            ostream(other.rdbuf())
        { }
};

int main() {
    cout << "hello world" << endl;

    myostream s(cout);
    s << "hello world" << endl;

    myostream(cout) << "hello world" << endl;
}

La salida, tanto en g++ y Visual C++, es

hello world
hello world
0x4012a4

La versión que se escribe en un objeto temporal, myostream(cout), parece que prefieren el miembro del operador ostream::operator<<(void *), en lugar de la libre operador operator<<(ostream &, char *).Parece que para hacer una diferencia si el objeto tiene un nombre.

¿Por qué sucede esto?Y ¿cómo puedo evitar este comportamiento?

Editar:Por qué sucede ahora es claro a partir de diferentes respuestas.Cómo prevenir esto, el siguiente parece atractiva:

class myostream : public ostream {
    public:
        // ...
        myostream &operator<<(char const *str) {
            std::operator<<(*this, str);
            return *this;
        }
};

Sin embargo, esto se traduce en todo tipo de ambigüedades.

¿Fue útil?

Solución

rvalues no puede ser obligado a no-const de referencia.Así, en el ejemplo el temporal de tipo ostream no puede ser el primer argumento de libre operador<<(std::ostream&, const char*) y lo que se utiliza es el socio operador<<(void*).

Si usted lo necesita, usted puede agregar una llamada, tales como

myostream(cout).flush() << "foo";

que va a transformar el r-value en una referencia.

Tenga en cuenta que en C++0X, la introducción de r-value de referencia permitirá abastecer la sobrecarga de operador<< tomando r-value referencias como parámetro, la resolución de la causa raíz del problema.

Otros consejos

Si un objeto no tiene un nombre (es decir, es una temporal), no se puede enlazar a una referencia no const. Específicamente, no se puede unir al primer parámetro de:

operator<<(ostream &, char *)

Me acabo de dar cuenta parte de la respuesta. El temporal no es un valor-I, por lo que no puede ser utilizado como un argumento de tipo ostream &.

La pregunta "¿cómo puedo hacer este trabajo" sigue siendo ...

Dado que ninguna de las respuestas hasta ahora parecen dar una solución limpia, me conformaré con la solución sucia:

myostream operator<<(myostream stream, char const *str) {
    std::operator<<(stream, str);
    return stream;
}

Esto sólo es posible porque myostream tiene un constructor de copia. (Internamente, está respaldado por una std::stringbuf ref-contado.)

Mientras que C ++ 11 no resolver este problema, ya que hay referencias rvalue, creo que esto podría ser una solución para pre-C ++ 11.

La solución es tener una función miembro << operador donde podemos fundido a una referencia no const a la clase base:

class myostream : public ostream {
    public:
        // ...
        template<typename T>
        ostream &operator<<(const T &t) {
            //now the first operand is no longer a temporary,
            //so the non-member operators will overload correctly
            return static_cast<ostream &>(*this) << t;
        }
};

Bueno, no sé la especificación C ++ que causa esto, pero es fácil de suss por qué sucede.

A vidas temporales en la pila, por lo general que se pasa a otra función o tener una sola operación llamada en él. Por lo tanto, si se llama al operador libre en él:

operador << (myostream (cout))

Se destruye al final de esta operación y la segunda "<<" operador para anexar la endl haría referencia a un objeto no válido. El valor de retorno de la "<<" operador libre sería una referencia a un objeto temporal Destructed. La especificación C ++, probablemente, define reglas sobre los operadores libres para evitar este escenario de programadores frustrante y confuso C ++.

Ahora, en el caso de un "<< (void *)" operador de miembro del temporal, el valor de retorno es el objeto en sí mismo, que todavía está en la pila y no se destruye, por lo que el compilador sabe que no debe destruir lo pero para pasar al siguiente operador miembro, el que lleva el endl. Operador de encadenamiento de los temporales es una característica útil para C ++ sucinta código, por lo que estoy seguro que los diseñadores de especificaciones C ++ consideraron e implementan el compilador para apoyar de manera intencional.

editar

Algunos han dicho que tiene que ver con una referencia no const. Este código se compila:

#include <iostream>
using namespace std;
class myostream : public ostream { 
    public: 
        myostream(ostream const &other) : 
            ostream(other.rdbuf()) 
        { } 
            ~myostream() { cout << " destructing "; }
    }; 
int _tmain(int argc, _TCHAR* argv[])
{
    basic_ostream<char>& result = std::operator << (myostream(cout), "This works");
    std::operator << (result, "illegal");
         return 0;
}

Y vuelve

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