template operatoren/funktionen



  • Hi,
    kann mir jemand sagen, warum diese Dekleration funktioniert:

    friend Matrix<T> operator+ <T>(const Matrix<T> &A, const Matrix<T> &B);
    

    diese aber nicht:

    friend Matrix<T> operator* <T>(const Matrix<T> &A, const Matrix<T> &B);
    

    wenn ich das ganze mit einer funktion versuche, klappts auch nicht:

    friend Matrix<T> matrixmult <T>(const Matrix<T> &A, const Matrix<T> &B);
    

    Matrix<T> ist meine Template Klasse, in der ich die oben genannten Operatoren/Funktionen deklarieren möchte.



  • 1. Was heisst für dich funktionieren?

    2. Compilerangaben, sowie einen besseren Kontext wäre auch hilfreich

    3. Ist Matrix ein Klassentemplate und keine Templateklasse

    4. Was soll das <T> nach dem Operator überhaupt? - Was soll das bezwecken?



  • Sorry, dachte es gäbe möglicherweise einen allgemeinen unterschied zwischen dem operator * und +

    was deine 4. frage betrifft:
    das <T> nach dem operator definiert damit ein operatortemplate (http://www.hlrs.de/people/mueller/tutorials/script/scriptse22.html)

    also meine Matrix Klasse sieht etwa so aus:
    ( Matrix greift auf die TNT library zu http://math.nist.gov/tnt/tnt_3_0_11.zip)

    #include "tnt.h" 
    #include <assert.h>
    
    template <class temp>
    class Matrix
    {
    private:
        unsigned long itsRows; 
        unsigned long itsColumns; 
    	TNT::Array2D<temp> itsArray2D; // no pointer
    
    public:
        Matrix(unsigned long rows, unsigned long columns);
        ~Matrix();
    
        //operator
        inline temp& operator()(unsigned long i, unsigned long j);
    	inline Matrix<temp> & Matrix<temp>::operator=(const temp &a);
    	friend std::ostream& operator<< <>(std::ostream &s, const Matrix<temp> &matrix1);
    	friend std::istream& operator>> <>(std::istream &s, Matrix<temp> &A);
    
    	friend Matrix<temp> operator+ <>(const Matrix<temp> &A, const Matrix<temp> &B);
    	friend Matrix<temp> operator- <>(const Matrix<temp> &A, const Matrix<temp> &B);
    
    };
    
    // C O N S T R U C T O R  for matrixes
    template<class temp>
    Matrix<temp>::Matrix(unsigned long rows, unsigned long columns)
      : itsRows(rows)
      , itsColumns(columns)
      , itsArray2D(rows, columns)
    {
      // object directly initialized
    }
    
    // D E S T R U C T O R  for matrixes
    template<class temp>
    Matrix<temp>::~Matrix(){}
    
    //O P E R A T O R  overloading
    // Operator ()
    template<class temp>
    inline temp& Matrix<temp>::operator ()(unsigned long i, unsigned long j)
    {
    	assert(i >= 0);
    	assert(j >= 0);
    	assert(i < itsRows);
    	assert(j < itsColumns);
        return itsArray2D[i][j];
    }
    
    // Operator =
    template <class temp>
    Matrix<temp> & Matrix<temp>::operator=(const temp &a)
    {
    	itsArray2D = a;
    	return *this;
    }
    
    // Operator <<
    template <class temp>
    std::ostream& operator<< <>(std::ostream &s, const Matrix<temp> &A)
    {
        ...
        return s;
    }
    
    // Operator >>
    template <class temp>
    std::istream& operator>> <>(std::istream &s, Matrix<temp> &A)
    {
        ...
        return s;
    }
    
    // Operator +
    template <class temp>
    Matrix<temp> operator+ <>(const Matrix<temp> &A, const Matrix<temp> &B)
    {
    	assert( A.itsArray2D.dim1() == B.itsArray2D.dim1() );
    	assert( A.itsArray2D.dim2() == B.itsArray2D.dim2() );
    	Matrix<temp> C( A.itsRows, A.itsColumns );
    	C.itsArray2D = A.itsArray2D + B.itsArray2D;
    	return C;
    }
    
    // Operator -
    template <class temp>
    Matrix<temp> operator- <>(const Matrix<temp> &A, const Matrix<temp> &B)
    {
    	assert( A.itsArray2D.dim1() == B.itsArray2D.dim1() );
    	assert( A.itsArray2D.dim2() == B.itsArray2D.dim2() );
    	Matrix<temp> C( A.itsRows, A.itsColumns );
    	C.itsArray2D = A.itsArray2D - B.itsArray2D;
    	return C;
    
    }
    

    soweit funktioniert alles (d.h. ich kanns starten und die matrix mit zB int-werten füllen, zwei matrizen addieren/subtrahieren ...)

    jetzt möcht ich halt auf die gleiche weise die elementmultiplikation bzw per funktion die richtige matrizenmultiplikation einbauen, der compiler ist aber damit anscheinend nicht einverstanden.
    er bringt einen fehler, welcher lautet: "Syntaxfehler: Es fehlt ';' vor '<'" , aber nur für die operatorüberladung von * bzw die funktionsdeklaration (matrixmult).

    so... hoffe ich hab nichts vergessen 😉


  • Administrator

    Reihenfolgen beachten oder andere Möglichkeiten in betracht ziehen, das löst deine Probleme. Wenn wir bei deiner Methode bleiben, dann musst du es so machen:

    // Predeclaration
    template<typename T>
    class Matrix;
    
    // Template operators definition
    template<typename T>
    Matrix<T> operator +(Matrix<T> const& lhs, Matrix<T> const& rhs)
    {
      // ...
    }
    
    // ...
    
    template<typename T>
    Matrix<T> operator *(Matrix<T> const& lhs, Matrix<T> const& rhs)
    {
      // ...
    }
    
    // ...
    
    template<typename T>
    class Matrix
    {
      // ...
      friend Matrix<T> operator + <>(Matrix<T> const& lhs, Matrix<T> const& rhs);
      friend Matrix<T> operator - <>(Matrix<T> const& lhs, Matrix<T> const& rhs);
      friend Matrix<T> operator * <>(Matrix<T> const& lhs, Matrix<T> const& rhs);
      friend Matrix<T> operator / <>(Matrix<T> const& lhs, Matrix<T> const& rhs);
    };
    

    Du kannst das aber auch anders lösen, allerdings ist der Effekt ein wenig anders, das Resultat aber dasselbe.

    template<typename T>
    class Matrix
    {
      // ...
      template<typename T>
      friend Matrix<T> operator +(Matrix<T> const& lhs, Matrix<T> const& rhs);
    
      template<typename T>
      friend Matrix<T> operator -(Matrix<T> const& lhs, Matrix<T> const& rhs);
    
      template<typename T>
      friend Matrix<T> operator *(Matrix<T> const& lhs, Matrix<T> const& rhs);
    
      template<typename T>
      friend Matrix<T> operator /(Matrix<T> const& lhs, Matrix<T> const& rhs);
    };
    
    template<typename T>
    Matrix<T> operator +(Matrix<T> const& lhs, Matrix<T> const& rhs)
    {
      // ...
    }
    
    // ...
    
    template<typename T>
    Matrix<T> operator *(Matrix<T> const& lhs, Matrix<T> const& rhs)
    {
      // ...
    }
    
    // ...
    

    Du deklarierst deine Klasse nun als Freund von allen Templates möglichkeiten. Also eine Matrix<double> hat im operator + auch internen Zugriff auf eine Matrix<int>. Da dies aber gar nie eintritt, ist das egal 😉

    Eine letzte Möglichkeit gibt es noch, damit du die friends ganz wegbekommst:

    template<typename T>
    class Matrix
    {
      // ...
      Matrix& operator +=(Matrix const& matrix);
      Matrix& operator -=(Matrix const& matrix);
      Matrix& operator *=(Matrix const& matrix);
      Matrix& operator /=(Matrix const& matrix);
    };
    
    template<typename T>
    Matrix<T> operator +(Matrix<T> const& lhs, Matrix<T> const& rhs)
    {
      Matrix<T> copy(lhs);
      copy += rhs;
      return copy;
    }
    
    // ...
    
    template<typename T>
    Matrix<T> operator *(Matrix<T> const& lhs, Matrix<T> const& rhs)
    {
      Matrix<T> copy(lhs);
      copy += rhs;
      return copy;
    }
    
    // ...
    

    Ich hoffe das hilft weiter, ansonsten fragen 😉

    Grüssli

    PS: Dem Tutorial Autor sollte man ein Feedback schreiben, dass seine Formatierungen äusserst unleserlich sind 🙂



  • Hmm.

    Dravere lass beim ersten Beispiel mal die Spitzen klammern weg, dann gehts und dein zweites Beispiel ist Mehrdeutig.. (2 mal typename T ?!)


  • Administrator

    drakon schrieb:

    Dravere lass beim ersten Beispiel mal die Spitzen klammern weg, dann gehts und dein zweites Beispiel ist Mehrdeutig.. (2 mal typename T ?!)

    Nein, die spitzen Klammern müssen hin, sonst geht es eben nicht. Und das zweite Beispiel ist bei mir nicht mehrdeutig. Das zweite T überdeckt das erstere. Muss allerding gestehen, dass ich nicht weiss, ob das auch Standardkonform ist. Der MSVC schluckt es jedenfalls 😉

    Grüssli



  • so... erstmal herzlichen dank 😉

    also... die zweite möglichkeit hat bei mir auch funktioniert
    aber mit der ersten möglichkeit (per vorwärtsdekleration) haperts...
    da bekomm ich gleich einen fehler zum konstruktor:

    error C2039: '{ctor}': Ist kein Element von 'Matrix<temp>'

    was dieses {ctor} ist, weiß ich leider nicht

    wenn ich den kon- und destruktor unter die gesamte klasse verschiebe, dann bekomm ich gleich bei der operatorüberladung von () probleme

    error C2039: '()': Ist kein Element von 'Matrix<temp>'

    was die dritte möglichkeit betrifft, so bilde ich mir ein (wenn ich keinen denkfehler habe), dass sie bei größeren matrizen langsamer ist, da ich erstmal die eine matrix kopieren muss

    achja... was ist eigentlich der unterschied zwischen

    template <class T>
    

    und

    template <typename T>
    

    außerdem konnte ich bei deiner zweiten möglichkeit sowas machen:

    template<class temp>
    class Matrix
    {
      // ...
      template<typename T>
      friend Matrix<temp> operator +(Matrix<temp> const& lhs, Matrix<temp> const& rhs);
    
      template<typename T>
      friend Matrix<temp> operator -(Matrix<temp> const& lhs, Matrix<temp> const& rhs);
    
      template<typename T>
      friend Matrix<temp> operator *(Matrix<temp> const& lhs, Matrix<temp> const& rhs);
    
      template<typename T>
      friend Matrix<temp> operator /(Matrix<temp> const& lhs, Matrix<temp> const& rhs);
    };
    

    und es hat trotzdem funktioniert.... versteh ich irgendwie nicht 😕


  • Administrator

    Kleriker schrieb:

    aber mit der ersten möglichkeit (per vorwärtsdekleration) haperts...
    da bekomm ich gleich einen fehler zum konstruktor:

    error C2039: '{ctor}': Ist kein Element von 'Matrix<temp>'

    was dieses {ctor} ist, weiß ich leider nicht

    Da fehlt ein Konstruktor. Zeig doch mal die operatoren, wie du sie definiert hast, allso alles was du nun vor die Klasse hingeschrieben hast.

    Kleriker schrieb:

    was die dritte möglichkeit betrifft, so bilde ich mir ein (wenn ich keinen denkfehler habe), dass sie bei größeren matrizen langsamer ist, da ich erstmal die eine matrix kopieren muss

    Schlaumeier, du musst so oder so eine Kopie bei einer normalen Addition oder was auch immer durchführen. Du darfst ja keine der übergebenen Parametern verändern, deshalb sind die auch konstant.

    Kleriker schrieb:

    achja... was ist eigentlich der unterschied zwischen

    template <class T>
    

    und

    template <typename T>
    

    Das erstere ist die "alte" Version. Danach wurde das Schlüsselwort typename eingeführt. In diesem Kontext sind die beiden Varianten aber gleich. Es gibt einen Fall, wo man class hinschreiben muss und es mit typename nicht funktioniert:

    template<template<typename T> class X>
    

    Das sind sogenannte Template-Templates. Wird aber im allgemeinen gemieden.

    Kleriker schrieb:

    außerdem konnte ich bei deiner zweiten möglichkeit sowas machen:

    // ...
    

    und es hat trotzdem funktioniert.... versteh ich irgendwie nicht 😕

    Weiss nicht, wie Standardkonform dies ist. Hab es noch nie so gesehen und in meinem Buch hier steht auch nix davon. Kann ich also nichts dazu sagen ...

    Grüssli



  • Dravere schrieb:

    Kleriker schrieb:

    was die dritte möglichkeit betrifft, so bilde ich mir ein (wenn ich keinen denkfehler habe), dass sie bei größeren matrizen langsamer ist, da ich erstmal die eine matrix kopieren muss

    Schlaumeier, du musst so oder so eine Kopie bei einer normalen Addition oder was auch immer durchführen. Du darfst ja keine der übergebenen Parametern verändern, deshalb sind die auch konstant.

    nein ich meine du hast einen kopiervorgang einer (möglicherweise) größeren matrix extra
    ansonsten würde man eine leere! matrix erstellen und die elemente einfügen, so erstellt man eine gefüllte matrix und füllt sie erneut

    nun zu dem fehler:

    #include "tnt.h"
    #include <assert.h> //assert check bounces just in debug mode
    
    template <class temp>
    class Matrix;
    
    // C O N S T R U C T O R  for matrixes
    template<class temp>
    Matrix<temp>::Matrix(unsigned long rows, unsigned long columns)
      : itsRows(rows)
      , itsColumns(columns)
      , itsArray2D(rows, columns)
    {
      // object directly initialized
    }
    
    // D E S T R U C T O R  for matrixes
    template<class temp>
    Matrix<temp>::~Matrix(){}
    
    //O P E R A T O R  overloading
    // Operator ()
    template<class temp>
    inline temp& Matrix<temp>::operator ()(unsigned long i, unsigned long j)
    {
    	assert(i >= 0);
    	assert(j >= 0);
    	assert(i < itsRows);
    	assert(j < itsColumns);
        return itsArray2D[i][j];
    }
    
    // Operator () const
    template<class temp>
    inline const temp& Matrix<temp>::operator()(unsigned long i, unsigned long j) const
    {
    	assert(i >= 0);
    	assert(j >= 0);
    	assert(i < itsRows);
    	assert(j < itsColumns);
        return itsArray2D[i][j];
    }
    
    // Operator =
    template <class temp>
    Matrix<temp> & Matrix<temp>::operator=(const temp &a)
    {
    	itsArray2D = a;
    	return *this;
    }
    
    // Template Operator <<
    template <class temp>
    std::ostream& operator<< <>(std::ostream &s, const Matrix<temp> &A)
    {
    	int M = A.itsArray2D.dim1();
    	int N = A.itsArray2D.dim2();
    
    //    s << M << " " << N << "\n";
    
    	for (int i=0; i<M; i++)
    	{
    		for (int j=0; j<N; j++)
    		{
    			s << A.itsArray2D[i][j] << " ";
    		}
    		s << "\n";
    	}
        return s;
    }
    
    // Template Operator >>
    template <class temp>
    std::istream& operator>> <>(std::istream &s, Matrix<temp> &A)
    {
    
        int M, N;
    
        s >> M >> N;
    
    	TNT::Array2D<temp> B(M,N);
    
        for (int i=0; i<M; i++)
            for (int j=0; j<N; j++)
            {
                s >>  B[i][j];
            }
    
    	A.itsArray2D = B;
        return s;
    }
    
    // Template Operator +
    template <class temp>
    Matrix<temp> operator+ <>(const Matrix<temp> &A, const Matrix<temp> &B)
    {
    	assert( A.itsArray2D.dim1() == B.itsArray2D.dim1() );
    	assert( A.itsArray2D.dim2() == B.itsArray2D.dim2() );
    	Matrix<temp> C( A.itsRows, A.itsColumns );
    	C.itsArray2D = A.itsArray2D + B.itsArray2D;
    	return C;
    }
    
    // Template Operator -
    template <class temp>
    Matrix<temp> operator- <>(const Matrix<temp> &A, const Matrix<temp> &B)
    {
    	assert( A.itsArray2D.dim1() == B.itsArray2D.dim1() );
    	assert( A.itsArray2D.dim2() == B.itsArray2D.dim2() );
    	Matrix<temp> C( A.itsRows, A.itsColumns );
    	C.itsArray2D = A.itsArray2D - B.itsArray2D;
    	return C;
    
    }
    
    // Template Operator *
    template <class temp>
    Matrix<temp> operator* (const Matrix<temp> &A, const Matrix<temp> &B)
    {
    	assert( A.itsArray2D.dim1() == B.itsArray2D.dim1() );
    	assert( A.itsArray2D.dim2() == B.itsArray2D.dim2() );
    	Matrix<temp> C( A.itsRows, A.itsColumns );
    	C.itsArray2D = A.itsArray2D * B.itsArray2D;
    	return C;
    }
    
    // Template Operator /
    template <class temp>
    Matrix<temp> operator/ (const Matrix<temp> &A, const Matrix<temp> &B)
    {
    	assert( A.itsArray2D.dim1() == B.itsArray2D.dim1() );
    	assert( A.itsArray2D.dim2() == B.itsArray2D.dim2() );
    	Matrix<temp> C( A.itsRows, A.itsColumns );
    	C.itsArray2D = A.itsArray2D / B.itsArray2D;
    	return C;
    }
    

    dann kommt die klasse an sich

    template <class temp>
    class Matrix
    {
    private:
        unsigned long itsRows; 
        unsigned long itsColumns; 
    	TNT::Array2D<temp> itsArray2D; // no pointer
    
    public:
        Matrix(unsigned long rows, unsigned long columns);
        ~Matrix();
    
        //operator
        inline temp& operator()(unsigned long i, unsigned long j);
    
    	inline const temp& operator ()(unsigned long i, unsigned long j) const;
    	inline Matrix<temp> & Matrix<temp>::operator=(const temp &a);
    	friend std::ostream& operator<< <>(std::ostream &s, const Matrix<temp> &matrix1);
    	friend std::istream& operator>> <>(std::istream &s, Matrix<temp> &A);
    	friend Matrix<temp> operator+ <>(const Matrix<temp> &A, const Matrix<temp> &B);
    	friend Matrix<temp> operator- <>(const Matrix<temp> &A, const Matrix<temp> &B);
    
    	friend Matrix<temp> operator* <>(const Matrix<temp> &A, const Matrix<temp> &B);
    	friend Matrix<temp> operator/ <>(const Matrix<temp> &A, const Matrix<temp> &B);
    
    };
    

    hoffe das war jetzt nicht zu viel code 😉


  • Administrator

    Kleriker schrieb:

    nein ich meine du hast einen kopiervorgang einer (möglicherweise) größeren matrix extra
    ansonsten würde man eine leere! matrix erstellen und die elemente einfügen, so erstellt man eine gefüllte matrix und füllt sie erneut

    Überleg mal die Schritte ...
    Deine Version:
    1. Speicher reservieren
    2. Speicher mit leer initialisieren (also z.B. Nullen)
    3. Addition für jedes Element aus Matrix A und B
    4. Resultat speichern -> zu 3. bis jedes Element durch ist.
    5. Matrix zurückgeben.

    Meine Version:
    1. Speicher reservieren
    2. Speicher mit Matrix A initialisieren
    3. Zu jedem Element in der Matrix, das Element aus der Matrix B addieren.
    -> wiederholen für alle Elemente
    4. Matrix zurückgeben.

    Womöglich funktioniert das meinige sogar besser, da ich keinen temporären Speicher für die Addition der Elemente brauche, sondern gleich auf dem reservierten Speicher operiere. 😉

    Zu deinem Fehler:
    Schau doch bitte nochmals genau hin, was ich geschrieben habe. Da sind nur die freien Operatoren vorne dran. Nicht auch alle Memberfunktionen!
    Zudem braucht es bei der freien Funktion keine spitzen Klammern nach dem Funktionsnamen. Diese sind nur in der friend Deklaration nötig. Steht übrigens auch alles so im Tutorial, welches du liest 😉

    Grüssli



  • hmmm... jetzt stehn nur die 2 operatoren über der klasse und es funktioniert... aber ich weiß ehrlich gesagt nicht warum. 😕
    wieso muss ich das nur bei den * und / operatoren machen?
    wenn ich jetzt weitere funktionen einbauen möchte, müssen diese auch drüber, oder?
    find das aber nicht besonders angenehm, wenn die hälfte des codes über und die andere hälfte unter der klasse ist 😡

    was die spitzen klammern betrifft, in dem fall muss ich sie ja drinlassen (darum gings ja in möglichkeit eins), wenn ich sie rausnehme dann bekomm ich einen fehler (irgendwas mit main.obj).

    ich denke ich werde die zweite möglichkeiit nutzen. wär nur nett, wenn du mir sagen kannst, warum das so ist (bei der ersten)

    und was den kopiervorgang angeht... hast schon recht, dauert wirklich nicht länger 😉


  • Administrator

    Kleriker schrieb:

    hmmm... jetzt stehn nur die 2 operatoren über der klasse und es funktioniert... aber ich weiß ehrlich gesagt nicht warum. 😕
    wieso muss ich das nur bei den * und / operatoren machen?
    wenn ich jetzt weitere funktionen einbauen möchte, müssen diese auch drüber, oder?

    Alle freien template Funktionen, welche als friend deklariert werden sollen, müssen vor die Klasse hin, bei diesem Verfahren.
    Wieso das so ist, frag das Standardkomittee. Mir persönlich ist es ein Rätsel, aber es ist so ... punkt ^^

    Kleriker schrieb:

    find das aber nicht besonders angenehm, wenn die hälfte des codes über und die andere hälfte unter der klasse ist 😡

    Deshalb hatte ich die anderen Möglichkeiten erwähnt, weil ich mir schon dachte, dass du dies nicht gut finden wirst. Wer tut das schon? 🙂

    Kleriker schrieb:

    was die spitzen klammern betrifft, in dem fall muss ich sie ja drinlassen (darum gings ja in möglichkeit eins), wenn ich sie rausnehme dann bekomm ich einen fehler (irgendwas mit main.obj).

    1. Nein, du musst die spitzen Klammern nur bei der Frienddeklaration drin lassen. Bei der Definition der freien template Funktionen, müssen die spitzen Klammern nach dem Namen raus. Um ehrlich zu sein, verwundert es mich sogar, dass es dies nicht als Syntaxfehler angibt.
    2. Das "irgendwas mit main.obj" ist eine nicht aufgelöste Funktion, welche der Linker dir dann zuwirft, da er die Funktion nicht finden kann. Aber das ist nur dann, wenn du die spitzen Klammern bei der friend Deklaration vergisst, also die Dinger in der Klasse drin ...

    Kleriker schrieb:

    ich denke ich werde die zweite möglichkeiit nutzen. wär nur nett, wenn du mir sagen kannst, warum das so ist (bei der ersten)

    Ist der Standard, welcher das so definiert ... die Erklärung wird dir sicher nicht reichen, wenn dem so ist, dann muss ich dich an das ISO-C++ Komittee weiterschicken 😉

    Kleriker schrieb:

    und was den kopiervorgang angeht... hast schon recht, dauert wirklich nicht länger 😉

    Und wieso nutzt du dann nicht gleich diese Methode? Die ist nämlich Codesparender. Du wirst nur einmal eine Addition implementieren müssen, nämlich für += , bei der + Operation kannst du auf diesen Code zurückgreifen. Finde ich persönlich angenehmer zu programmieren, als bei der += und + Operation Code für die Addition zu implementieren. Und das gilt natürlich immer für alle Operationen 😉

    Grüssli


Anmelden zum Antworten