Wie kann man den

Lesezeit: 1 Minute

Wie kann man den
Mathias van der Vlies

Ich schreibe eine kleine Matrixbibliothek in C++ für Matrixoperationen. Allerdings beschwert sich mein Compiler, wo vorher nicht. Dieser Code wurde 6 Monate lang in einem Regal gelassen und zwischendurch habe ich meinen Computer von Debian Etch auf Lenny (g++ (Debian 4.3.2-1.1) 4.3.2 ) aktualisiert, aber ich habe das gleiche Problem auf einem Ubuntu-System mit demselben g++ .

Hier ist der relevante Teil meiner Matrixklasse:

namespace Math
{
    class Matrix
    {
    public:

        [...]

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

Und die “Umsetzung”:

using namespace Math;

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

    [...]

}

Dies ist der vom Compiler ausgegebene Fehler:

matrix.cpp:459: Fehler: ‘std::ostream& Math::Matrix::operator<<(std::ostream&, const Math::Matrix&)' muss genau ein Argument haben

Ich bin ein bisschen verwirrt von diesem Fehler, aber andererseits ist mein C++ etwas eingerostet, nachdem ich in diesen 6 Monaten viel Java gemacht habe. 🙂

Nur um Ihnen von einer anderen Möglichkeit zu erzählen: Ich verwende dafür gerne Freundschaftsdefinitionen:

namespace Math
{
    class Matrix
    {
    public:

        [...]

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

Die Funktion wird automatisch auf den umgebenden Namespace ausgerichtet Math (obwohl seine Definition innerhalb des Geltungsbereichs dieser Klasse erscheint), aber nicht sichtbar ist, es sei denn, Sie rufen operator<< mit einem Matrix-Objekt auf, wodurch eine argumentabhängige Suche diese Operatordefinition findet. Das kann manchmal bei mehrdeutigen Aufrufen hilfreich sein, da es für andere Argumenttypen als Matrix unsichtbar ist. Beim Schreiben seiner Definition können Sie auch direkt auf in Matrix definierte Namen und auf Matrix selbst verweisen, ohne den Namen mit einem möglicherweise langen Präfix zu qualifizieren und Vorlagenparameter wie bereitzustellen Math::Matrix<TypeA, N>.

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

  • Und Sie sollten es auch innerhalb des Math-Namespace deklarieren (nicht nur mit einem using-Namespace Math).

    – David Rodríguez – Dribeas

    23. März 2009 um 21:35 Uhr

  • Warum tut die operator<< müssen sich im Namensraum von befinden Math? Es scheint, dass es im globalen Namespace sein sollte. Ich stimme zu, dass mein Compiler möchte, dass es sich im Namespace von befindet Mathaber das ergibt für mich keinen Sinn.

    – Mark Lakata

    1. Mai 2015 um 21:58 Uhr

  • Tut mir leid, aber ich verstehe nicht, warum wir hier dann das Schlüsselwort Freund verwenden? Wenn in einer Klasse das Überschreiben von Friend-Operatoren deklariert wird, können wir anscheinend nicht mit Matrix::operator<<(ostream& os, const Matrix& m) implementieren. Stattdessen müssen wir einfach den globalen Operator override operator<

    – Patrick

    2. August 2019 um 16:59 Uhr


Wie kann man den
Kal

Um die Mehrdad-Antwort zu ergänzen,

namespace Math
{
    class Matrix
    {
       public:

       [...]


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

In Ihrer Umsetzung

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

  • Ich verstehe nicht, warum dies eine Ablehnung ist. Dies verdeutlicht, dass Sie den Operator als im Namensraum und nicht einmal als Freund deklarieren können und wie Sie den Operator möglicherweise deklarieren können.

    – kal

    24. Januar 2009 um 20:28 Uhr

  • Die Mehrdad-Antwort hatte kein Code-Snippet, also habe ich einfach hinzugefügt, was funktionieren könnte, indem ich es außerhalb der Klasse im Namespace selbst verschoben habe.

    – kal

    24. Januar 2009 um 20:30 Uhr

  • Ich verstehe Ihren Punkt, ich habe mir nur Ihren zweiten Ausschnitt angesehen. Aber jetzt sehe ich, dass Sie den Operator aus der Klasse genommen haben. Danke für den Vorschlag.

    – Matthias van der Vlies

    24. Januar 2009 um 20:32 Uhr

  • Es ist nicht nur außerhalb der Klasse, sondern es ist richtig definiert Innerhalb den Math-Namensraum. Es hat auch den zusätzlichen Vorteil (vielleicht nicht für eine Matrix, aber mit anderen Klassen), dass “Drucken” virtuell sein kann und daher das Drucken auf der am weitesten abgeleiteten Vererbungsebene erfolgt.

    – David Rodríguez – Dribeas

    23. März 2009 um 21:45 Uhr

1647106213 388 Wie kann man den
sanjivr

Angenommen, wir sprechen von Überlastung operator << für alle abgeleiteten Klassen std::ostream um die zu handhaben Matrix Klasse (und nicht überladen << zum Matrix Klasse), ist es sinnvoller, die Überladungsfunktion außerhalb des Math-Namespace im Header zu deklarieren.

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 die Operatorüberladung außerhalb des Namespace deklariert wird.

Matrix.cpp

using namespace Math;
using namespace std;

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

Andererseits, wenn Ihre Überlastfunktion tut muss zum Freund gemacht werden, dh braucht Zugriff auf private und geschützte Mitglieder.

Math.h

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

Sie müssen die Funktionsdefinition mit einem Namensraumblock einschließen, anstatt nur using namespace Math;.

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

1647106214 94 Wie kann man den
QuentinUK

In C++14 können Sie die folgende Vorlage verwenden, um jedes Objekt zu drucken, das eine T::print(std::ostream&)const; Mitglied.

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

In C++20 können Konzepte verwendet werden.

template<typename T>
concept Printable = requires(std::ostream& os, T const & t) {
    { t.print(os) };
};

template<Printable T>
std::ostream& operator<<(std::ostream& os, const T& t) { 
    t.print(os); 
    return os; 
} 

  • interessante Lösung! Eine Frage – wo sollte dieser Operator deklariert werden, wie in einem globalen Bereich? Ich nehme an, es sollte für alle Typen sichtbar sein, die zum Erstellen von Vorlagen verwendet werden können?

    – Barney

    16. Mai 2016 um 6:08 Uhr

  • @barney Es könnte sich zusammen mit den Klassen, die es verwenden, in Ihrem eigenen Namensraum befinden.

    – QuentinUK

    16. Mai 2016 um 23:30 Uhr

  • kannst du nicht einfach zurückkommen std::ostream&da es sowieso der Rückgabetyp ist?

    – Jean-Michaël Sellerie

    8. März 2017 um 20:16 Uhr

  • @Jean-MichaëlCelerier Der decltype stellt sicher, dass dieser Operator nur verwendet wird, wenn t::print vorhanden ist. Andernfalls würde es versuchen, den Funktionskörper zu kompilieren und einen Kompilierungsfehler geben.

    – QuentinUK

    12. März 2017 um 10:43 Uhr


  • Konzeptversion hinzugefügt, hier getestet godbolt.org/z/u9fGbK

    – QuentinUK

    4. Mai 2020 um 13:34 Uhr

Ich möchte dies mit einem überladenden Beispiel ein wenig vereinfachen << um ein Array zu drucken.

  1. Führen Sie zuerst beide Objekttypen um die herum << Operator
  2. Erstellen Sie eine Funktion zum Überladen des Operators wie folgt.
#include<iostream> 
using namespace std;

void operator<<(ostream& os, int arr[]) {
    for (int i = 0;i < 10;i++) {
        cout << arr[i] << " ";
    }
    cout << endl; 
}
    
int main() {
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    cout << arr;
}

Wenn auch eine Kaskadierung von Operatoren erforderlich ist, stellen Sie sicher, dass Sie zurückkehren cout Objekt in der überladenen Funktion wie folgt,

#include<iostream> 
using namespace std;

ostream& operator<<(ostream& os, int arr[]) {
    for (int i = 0;i < 10;i++) {
        cout << arr[i] << " ";
    }
    cout << endl; 
    return os;
}
    
int main() {
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int arr2[10] = { 11,22,33,44,55,66,77,88,99,100 };
    // cascading of operators
    cout << arr << arr2;
}

  • interessante Lösung! Eine Frage – wo sollte dieser Operator deklariert werden, wie in einem globalen Bereich? Ich nehme an, es sollte für alle Typen sichtbar sein, die zum Erstellen von Vorlagen verwendet werden können?

    – Barney

    16. Mai 2016 um 6:08 Uhr

  • @barney Es könnte sich zusammen mit den Klassen, die es verwenden, in Ihrem eigenen Namensraum befinden.

    – QuentinUK

    16. Mai 2016 um 23:30 Uhr

  • kannst du nicht einfach zurückkommen std::ostream&da es sowieso der Rückgabetyp ist?

    – Jean-Michaël Sellerie

    8. März 2017 um 20:16 Uhr

  • @Jean-MichaëlCelerier Der decltype stellt sicher, dass dieser Operator nur verwendet wird, wenn t::print vorhanden ist. Andernfalls würde es versuchen, den Funktionskörper zu kompilieren und einen Kompilierungsfehler geben.

    – QuentinUK

    12. März 2017 um 10:43 Uhr


  • Konzeptversion hinzugefügt, hier getestet godbolt.org/z/u9fGbK

    – QuentinUK

    4. Mai 2020 um 13:34 Uhr

994580cookie-checkWie kann man den

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy