2009-01-24 13 views
171

Ich schreibe eine kleine Matrixbibliothek in C++ für Matrixoperationen. Allerdings beschwert sich mein Compiler, wo vorher nicht. Dieser Code wurde für 6 Monate in einem Regal gelassen und zwischendurch habe ich meinen Computer von debian etch auf lenny aktualisiert (g ++ (Debian 4.3.2-1.1) 4.3.2 ), aber ich habe das gleiche Problem auf einem Ubuntu-System mit dem gleichen g ++.Wie kann man den << Operator für einen Ostream richtig überladen?

Hier ist der relevante Teil meiner Matrix-Klasse:

namespace Math 
{ 
    class Matrix 
    { 
    public: 

     [...] 

     friend std::ostream& operator<< (std::ostream& stream, const Matrix& matrix); 
    } 
} 

Und die "Umsetzung":

matrix.cpp:459: error: 'std::ostream& Math::Matrix::operator<<(std::ostream&, const Math::Matrix&)' must take exactly one argument

:

using namespace Math; 

std::ostream& Matrix::operator <<(std::ostream& stream, const Matrix& matrix) { 

    [...] 

} 

Dies ist der Fehler vom Compiler gegeben ist Ich bin ein wenig verwirrt durch diesen Fehler, aber dann wieder mein C++ ist ein bisschen eingerostet, nachdem ich viel Java diese 6 Mon gemacht habe ths. :-)

Antwort

99

Sie haben Ihre Funktion als friend deklariert. Es ist kein Mitglied der Klasse. Sie sollten Matrix:: aus der Implementierung entfernen. friend bedeutet, dass die angegebene Funktion (die kein Mitglied der Klasse ist) auf private Membervariablen zugreifen kann. Die Art, wie Sie die Funktion implementiert haben, ist wie eine Instanzmethode für die Klasse Matrix, die falsch ist.

+6

Und Sie sollten es auch im Math-Namespace deklarieren (nicht nur mit einem Verwenden von Namespace Math). –

+1

Warum muss der Operator '' im Namensraum von 'Math' stehen? Es scheint, dass es im globalen Namespace sein sollte. Ich stimme zu, dass mein Compiler es im Namensraum von 'Math' haben will, aber das ergibt für mich keinen Sinn. –

58

zu Mehrdad Antwort hinzuzufügen, sagen Sie

namespace Math 
{ 
    class Matrix 
    { 
     public: 

     [...] 


    } 
    std::ostream& operator<< (std::ostream& stream, const Math::Matrix& matrix); 
} 

in Ihrer Implementierung

std::ostream& operator<<(std::ostream& stream, 
        const Math::Matrix& matrix) { 
    matrix.print(stream); //assuming you define print for matrix 
    return stream; 
} 
+3

Ich verstehe nicht, warum ist dies ein Down-Vote, dies verdeutlicht, dass Sie Operator im Namespace und nicht einmal als Freund erklären können und wie Sie den Operator möglicherweise erklären können. – kal

+2

Mehrdad Antwort hatte keinen Code-Schnipsel, also fügte ich einfach hinzu, was funktionieren könnte, indem Sie es außerhalb der Klasse in den Namespace selbst verschieben. – kal

+0

Ich verstehe Ihren Punkt, ich habe nur Ihren zweiten Ausschnitt betrachtet. Aber jetzt sehe ich, dass du den Operator aus der Klasse genommen hast. Danke für den Vorschlag. –

114

nur über eine andere Möglichkeit: Ich mag für diesen Freund-Definitionen:

namespace Math 
{ 
    class Matrix 
    { 
    public: 

     [...] 

     friend std::ostream& operator<< (std::ostream& stream, const Matrix& matrix) { 
      [...] 
     } 
    }; 
} 

Die Funktion wird automatisch in den umgebenden Namensraum Math (e Obwohl ihre Definition innerhalb des Gültigkeitsbereichs dieser Klasse erscheint, ist sie nicht sichtbar, es sei denn, Sie rufen den Operator < < mit einem Matrix-Objekt auf, das die abhängigkeitsabhängige Suche nach dieser Operatordefinition durchführt. Das kann manchmal bei mehrdeutigen Aufrufen helfen, da es für andere Argumenttypen als Matrix unsichtbar ist. Beim Schreiben seiner Definition können Sie auch direkt auf Namen verweisen, die in Matrix und auf Matrix selbst definiert sind, ohne den Namen mit einem möglicherweise langen Präfix zu qualifizieren und Vorlagenparameter wie Math::Matrix<TypeA, N> bereitzustellen.

+0

Danke für diese Antwort war es sehr nützlich für mich. – tommyk

+4

Ich denke, diese Antwort ist besser als die angenommene. Vielen Dank! – PolyMesh

48

Unter der Annahme, dass wir reden über operator << für alle Klassen von std::ostream abgeleitet Überlastung der Matrix Klasse zu behandeln (und nicht << für Matrix Klasse Überlastung), ist es sinnvoller, die Überlastfunktion außerhalb des Math-Namensraumes im Header zu erklären .

Verwenden Sie eine Friend-Funktion nur, wenn die Funktionalität nicht über die öffentlichen Schnittstellen erreicht werden kann.

Matrix.h

namespace Math { 
    class Matrix { 
     //... 
    }; 
} 
std::ostream& operator<<(std::ostream&, const Math::Matrix&); 

Beachten Sie, dass der Betreiber Überlastung außerhalb des Namensraum deklariert.

Matrix.cpp

using namespace Math; 
using namespace std; 

ostream& operator<< (ostream& os, const Matrix& obj) { 
    os << obj.getXYZ() << obj.getABC() << '\n'; 
    return os; 
} 

Auf der anderen Seite, wenn Ihre Überlastfunktion tut Notwendigkeit, einen Freund das heißt Zugang muss private und geschützten Mitglieder.

math.h

namespace Math { 
    class Matrix { 
     public: 
      friend std::ostream& operator<<(std::ostream&, const Matrix&); 
    }; 
} 

Sie müssen die Funktionsdefinition mit einem Namespace-Block statt nur using namespace Math; einzuschließen.

Matrix.cpp

using namespace Math; 
using namespace std; 

namespace Math { 
    ostream& operator<<(ostream& os, const Matrix& obj) { 
     os << obj.XYZ << obj.ABC << '\n'; 
     return os; 
    }     
} 
20

In C++ 14 Sie die folgende Vorlage verwenden können, jedes Objekt zu drucken, die einen T :: Druck hat (std :: Ostream &) const; Mitglied.

template<class T> 
auto operator<<(std::ostream& os, const T& t) -> decltype(t.print(os), os) 
{ 
    t.print(os); 
    return os; 
} 
+1

Eigentlich scheint das auch in C++ 11 zu funktionieren. – jotik

+0

interessante Lösung! Eine Frage - wo sollte dieser Operator wie im globalen Rahmen deklariert werden? Ich nehme an, dass es für alle Arten sichtbar sein sollte, die verwendet werden können, um es zu templatisieren? – barney

+0

@ barney Es könnte in Ihrem eigenen Namensraum zusammen mit den Klassen sein, die es verwenden. – QuentinUK