Domanda

Considera il seguente esempio minimo:

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

L'output, sia su g++ che su Visual C++, è

hello world
hello world
0x4012a4

La versione che scrive su un oggetto temporaneo, myostream(cout), sembra preferire l'operatore membro ostream::operator<<(void *), invece dell'operatore gratuito operator<<(ostream &, char *).Sembra fare la differenza se l'oggetto ha o meno un nome.

Perché succede questo?E come posso prevenire questo comportamento?

Modificare:Il motivo per cui accade è ormai chiaro da varie risposte.Per quanto riguarda come prevenire ciò, sembra interessante quanto segue:

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

Tuttavia, ciò dà luogo a tutti i tipi di ambiguità.

È stato utile?

Soluzione

gli rvalue non possono essere associati a riferimenti non costanti.Quindi nel tuo esempio il temporaneo di tipo ostream non può essere il primo argomento dell'operatore libero<<(std::ostream&, char const*) e ciò che viene utilizzato è l'operatore membro<<(void*).

Se ne hai bisogno, puoi aggiungere una chiamata come

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

che trasformerà l'rvalue in un riferimento.

Si noti che in C++0X, l'introduzione del riferimento rvalue consentirà di fornire un sovraccarico dell'operatore<< prendendo i riferimenti rvalue come parametro, risolvendo la causa principale del problema.

Altri suggerimenti

Se un oggetto non ha un nome (cioè esso è un temporaneo), non può essere associato a un riferimento non const. In particolare, non può essere vincolata al primo parametro di:

operator<<(ostream &, char *)

Ho appena realizzato parte della risposta. Il temporaneo non è un lvalue, quindi non può essere utilizzato come un argomento di tipo ostream &.

La domanda "come posso fare questo lavoro" rimane ...

Dal momento che nessuna delle risposte finora sembra dare una soluzione pulita, mi accontenterò della soluzione sporca:

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

Questo è possibile solo perché myostream ha un costruttore di copia. (Internamente, è sostenuta da un std::stringbuf ref-contati.)

Mentre C ++ 11 non risolvere il problema in quanto ci sono riferimenti rvalue, credo che questo potrebbe essere una soluzione per il pre-C ++ 11.

La soluzione è quella di avere una funzione membro << operatore dove possiamo lanciare a un riferimento non const alla classe di 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;
        }
};

Beh, non so le specifiche C ++ che causa questo, ma è facile da suss fuori perché succede.

A vite temporanee nello stack, di solito per essere passato a un'altra funzione o di avere una singola operazione chiamata su di esso. Quindi, se si chiama l'operatore libero su di esso:

operatore << (myostream (cout))

Si distrutta alla fine di questa operazione e la seconda "<<" all'operatore di aggiungere l'endl sarebbe riferimento ad un oggetto non valido. Il valore restituito dal "<<" operatore libero sarebbe un riferimento a un oggetto temporaneo destructed. Le specifiche C ++ definisce probabilmente regole per gli operatori liberi per evitare questo scenario da programmatori frustrante e confusa C ++.

Ora, nel caso di un "<< (void *)" operatore membro sul temporaneo, il valore restituito è l'oggetto stesso, che è ancora in pila e non distrutto, in modo che il compilatore sa di non distruggere essa ma per passare al successivo operatore punto, quello che prende l'endl. Operatore concatenamento sul provvisori è una caratteristica utile per succinta codice C ++, quindi sono sicuro che i C ++ spec designer ritenuto e attuate al compilatore di sostenerlo intenzionalmente.

modifica

Alcuni hanno detto che è a che fare con un riferimento non-const. Questo codice viene compilato:

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

E ritorna

  This works destructing illegal
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top