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

-
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 ?!)
-
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

-
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
typenameeingeführt. In diesem Kontext sind die beiden Varianten aber gleich. Es gibt einen Fall, wo manclasshinschreiben muss und es mittypenamenicht 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 erneutnun 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

-
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

-
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