Frage

Betrachten Sie das folgende minimale Beispiel:

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

Die Ausgabe, die sowohl auf g++ und Visual C++ ist

hello world
hello world
0x4012a4

Die version, die schreibt, um ein temporäres Objekt, myostream(cout), scheint zu bevorzugen, die Mitglied Betreiber ostream::operator<<(void *), anstelle der freien Betreiber operator<<(ostream &, char *).Es scheint einen Unterschied zu machen, ob oder ob nicht das Objekt hat einen Namen.

Warum passiert das?Und wie kann ich verhindern, dass dieses Verhalten?

Bearbeiten:Warum es passiert, ist jetzt klar, aus verschiedenen Antworten.Wie um dies zu verhindern, wird das folgende scheint attraktiv:

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

Jedoch, diese Ergebnisse in allen Arten von Mehrdeutigkeiten.

War es hilfreich?

Lösung

rvalues kann nicht gebunden werden, um nicht-const-Referenz.Also in deinem Beispiel die vorübergehenden vom Typ ostream kann nicht sein das erste argument des freien operator<<(std::ostream&, char const*) und was verwendet wird, ist das Mitglied Betreiber<<(void*).

Wenn Sie es brauchen, Sie können einen Anruf wie

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

die verwandeln den in einen rvalue-Referenz.

Beachten Sie, dass in C++0X, die Einführung von rvalue-Referenz wird ermöglichen zu bieten überladung von operator<< die Einnahme von rvalue-Referenzen als parameter, die Lösung der Ursache des Problems.

Andere Tipps

Wenn ein Objekt keinen Namen hat (das heißt es ist ein temporärer), es kann nicht auf eine nicht konstante Referenz gebunden werden. Insbesondere kann es nicht auf den ersten Parameter von gebunden werden:

operator<<(ostream &, char *)

Ich habe gerade realisiert Teil die Antwort. Die temporäre ist kein L-Wert, so dass er nicht als Argument vom Typ ostream & verwendet werden kann.

Die Frage „Wie kann ich diese Arbeit zu machen“ bleibt ...

Da keine der Antworten bisher scheint eine saubere Lösung zu geben, ich werde für die schmutzige Lösung absetzen:

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

Dies ist nur möglich, weil myostream eine Kopie Konstruktor hat. (Intern wird es von einem ref gezählt std::stringbuf gesichert.)

Während C ++ 11 löst dieses Problem, da es rvalue Referenzen sind, denke ich, könnte dies eine Abhilfe für Pre-C ++ 11.

Die Lösung ist eine Memberfunktion haben << Operator, wo wir auf eine nicht konstante Referenz auf die Basisklasse werfen können:

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

Nun, ich weiß nicht, die C ++ Spezifikation, die dies verursacht, aber es ist einfach zu abtasten, warum es passiert.

Ein temporäres Leben auf dem Stapel, in der Regel an eine andere Funktion übergeben werden oder eine einzelne Operation auf sie aufgerufen haben. Also, wenn Sie den kostenlosen Bediener auf ihn nennen:

operator << (myostream (cout))

Es befindet sich am Ende dieser Operation und dem zweiten „<<“ Operator zerstört die endl anhängen würde ein ungültiges Objekt verweisen. Der Rückgabewert vom freien „<<“ Operator wäre ein Verweis auf ein destructed temporäres Objekt sein. Der C ++ spec wahrscheinlich definiert Regeln über die freien Betreiber dieses Szenario Programmierer aus frustrierend und verwirrend C ++ zu verhindern.

Nun, im Fall eines „<< (void *)“ Operators auf dem temporären, der Rückgabewert ist das Objekt selbst, das immer noch auf dem Stapel und nicht zerstört, so dass der Compiler nicht weiß, es zu zerstören aber es zum nächsten Mitglied Operator passieren, die eine, die die endl nimmt. Operator Chaining auf Provisorien ist eine nützliche Funktion für prägnante C ++ Code, also bin ich sicher, dass die C ++ spec Designer betrachtet es und implementiert, um den Compiler es absichtlich zu unterstützen.

Bearbeiten

Einige haben gesagt, dass es mit einer nicht konstanten Referenz zu tun. Dieser Code kompiliert:

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

Und es gibt

  This works destructing illegal
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top