Template Spezialisierung/Instantiierung
-
Deklariere und definiere operator*(const Feld F<T>&, double skalar<) als friend von der Templateklasse Feld und innerhalb dieser.
Etwa so:
template <typename T> class Feld { typedef Feld self; public: //... friend self operator*(const self& F, double skalar) { self result(F); for(int i = 0; i < F.size(); ++i) result[i] *= skalar; return result; } //... };ps: wobei ich für eine Skalarmultiplikation nur den operator*(double, const Feld&); anbieten würde. Andersrum sieht das komisch aus.
-
bmario schrieb:
Deklariere und definiere operator*(const Feld F<T>&, double skalar<) als friend von der Templateklasse Feld und innerhalb dieser.
Nein. Lass die globalen
operator*auf den Memberoperator*=zurückgreifen.Feld& Feld::operator*= (double rhs); Feld operator* (Feld lhs, double rhs); Feld operator* (double lhs, Feld rhs);bmario schrieb:
ps: wobei ich für eine Skalarmultiplikation nur den operator*(double, const Feld&); anbieten würde. Andersrum sieht das komisch aus.
"Komisch aussehen" ist kein Grund, die Kommutativität der Multiplikation (die zumindest hier vorhanden ist, falls
Feldeine Art Vektor ist) zu verletzen.
-
Nexus schrieb:
bmario schrieb:
Deklariere und definiere operator*(const Feld F<T>&, double skalar<) als friend von der Templateklasse Feld und innerhalb dieser.
Nein. Lass die globalen
operator*auf den Memberoperator*=zurückgreifen.Feld& Feld::operator*= (double rhs); Feld operator* (Feld lhs, double rhs); Feld operator* (double lhs, Feld rhs);Nexus schrieb:
bmario schrieb:
ps: wobei ich für eine Skalarmultiplikation nur den operator*(double, const Feld&); anbieten würde. Andersrum sieht das komisch aus.
"Komisch aussehen" ist kein Grund, die Kommutativität der Multiplikation (die zumindest hier vorhanden ist, falls
Feldeine Art Vektor ist) zu verletzen.Mit "komisch aussehen" meinte ich, dass die Skalarmultiplikation eines Vektorraumes im Allgemeinen eben nicht kommutativ ist. Allerdings gibt es Spezialfälle, für welche dies gilt, z.B. für den R-Vektorraum R^1.
-
der Ärger geht schon mit Literalen los. Literale werden
offenbar bei der Template-Instantiation anders behandelt
als Variablen. Z.B.template<class T, class D> feld<T> operator+(const feld<T>& Lhs,const D& rhs); template<class T, class D> feld<T> operator+(const feld<T>& Lhs,const D& rhs) { feld<T> Tmp; Tmp += Lhs; Tmp += rhs; return Tmp; } ... int main() { feld<float> A(10); int i=0; A+i; // geht, Variable, Funktionsinstantiation wird angelegt A+3.0; // geht nicht, Literal A+1; // geht nicht, Literal }Oberstes Ziel für mich ist, dass Ausdrücke natürlich geschrieben
werden können. Es reichst schon, dass die Mathematik kompliziert
ist, da brauche ich nicht noch eine nicht intuitive Syntax.Ich werde wohl Templates für den Feld-Typ verwenden, aber
für die skalaren Typen das Gemeinsame in eine Unterfunktion
packen und das Besondere vielleicht per Perl-Script aus
einem Prototypen auswalzen.Wenn die Tyen, die überhaupt benötigt werden, sehr überschaubar
sind und wenn sehr verschiedene Algorithmen je Typ
angewendet werden, dann verlieren Templates sehr stark an
Attraktivität. Für die Container der STL funktioniert das,
aber z. B. für eine FFT, die je nach Argumenttyp optimierte
Algorithmen loslässt, ist das eben nicht so.
Übrigens: wie kann eine Funktion den Typ des Arguments erkennen?
Wastl
-
,D const& rhs)Sonst klappt's nicht bei r-values.
-
@wastl: Eine Funktion "erkennt" den Typ des Templates nicht, für jeden Typ wird eine eigene Funktion vom Compiler generiert, deswegen muss der Typ auch zur Compilezeit bekannt sein. Übrigens klingt das, was du da sagst, einerseits unverständlich und andererseits so, als ob man dir durchaus noch helfen könnte, deshalb bitte ich dich nochmal, das Zeug mal genau zu erklären. Ein Funktionsbeispiel mit allen Features hast du auch noch nicht gemacht.
-
im letzten Beispiel habe ich ein Beispiel
für den Operator "+" angegeben.
Der Compiler bildet die richtige Funktion
aus dem Template, bzw. casted, wenn die Argumente
Variablen sind. Bei Literalen funktioniert
das Beispiel nicht. Also habe ich doch
ein Beispiel gegeben. (die const habe ich gefixt).Arithmetische Operationen enthalten i.d.R.
Literale, so dass eine natürliche Schreibweise
dadurch nicht möglich ist.Das ist schon ein nicht unerhebliches Teilproblem
des Ganzen und sollte zunächst betrachtet werden.I.d.R. werden immer Templates der Sorte
template<T> Feld<T>& Feld<T>::operator+=(Feld<T>& Rhs);betrachtet, für die gar kein Problem auftritt, weil
der Typ von Rhs mit dem Feldtyp übereinstimmt.
Wenn die Situation asymmetrisch wird, beginnen die
Probleme:template<T> Feld<T>& Feld<T>::operator+=(TYPE& Rhs);wobei Type irgendein skalarer Typ ist, eine Lösung
ist gesucht, die solche Skalaren Typen allesamt
abdeckt und auch mit Literalen zurecht kommt.
Dito für Nichtmitgliedfunktionen.
-
Am besten du machst die Übergabe immer da, wo es sinnvoll ist, als const-Referenz.
Auf Literale und temporäre Ausdrücke können keine veränderlichen Referenzen genommen werden.z.B.
class Vector; const Vector operator+(Vector& lhs, Vector& rhs); Vector v,w,z; //... Vector sum=v+w+z; //Fehler, es wird eine Referenz auf ein temporäres Objekt erzeugt Vector sum2 = sum + 2; //Fehler, es wird eine Referenz auf ein Literal erzeugt
-
Das mit den const ist schon klar, bloß hier
gespart beim Tippen.Ich habe das allgemein jetzt so gelöst:
template<T> class Feld { public: Feld(int n); ~Feld(); Feld& operator+=(const Feld& Rhs); // das ist der Normalfall, selber Typ Feld& operator+=(const double& rhs); // damit gehen Skalare und auch Literale Feld& operator+=(const float& rhs); // damit gehen Skalare Feld& operator+=(const int& rhs); // damit gehen Skalare und auch Literale Feld& operator+=(const uint32& rhs); // damit gehen Skalare ... };Für die Nichtmitglieder entsprechend, da gibt es noch mehr Varianten:
Feld<T> operator+(const Feld<T>& Lhs, const Feld<T>& Rhs); Feld<T> operator+(const Feld<T>& Lhs, const double& rhs); Feld<T> operator+(const Feld<T>& Lhs, const float& rhs); Feld<T> operator+(const Feld<T>& Lhs, const int& rhs); Feld<T> operator+(const Feld<T>& Lhs, const uint32& rhs); Feld<T> operator+(const double& lhs, const Feld<T>& Rhs); Feld<T> operator+(const float& lhs, const Feld<T>& Rhs); Feld<T> operator+(const int& lhs, const Feld<T>& Rhs); Feld<T> operator+(const uint32& lhs, const Feld<T>& Rhs);Schön ist auch "-" oder "/", weil nicht kommutativ, also dann
3 Algorithmen: für Feld<T>, für Skalar links und Skalar rechts.
Na und wenn T komplexe Zahlen repräsentiert, dann machen
viele Kombinationen wenig Sinn. Spezialisierung zur
Elimination solcher Varianten ??Ich präzisiere meine Frage jetzt: wie kann ich die mehrfache
Anlage für jeden Operator sparen? Dass es für einen Vektor
anders ist als für einen Skalar, das ist ja klar, anderer Code.
Aber wenigstens die skalaren Typen sollte man doch irgendwie
zusammen erschlagen können.template<T,D>Konstruktionen helfen nicht bei Literalen und auch sonst,
auch kam es zum Überladen von Operatoren per Mehrfachinstantiation
durch den Compiler. Das habe ich schon probiert.
Das aus-x-en wie oben funktioniert, das weiß ich sicher.Man muss es probieren, denn vieles funktioniert in der
Praxis doch nicht, z. B. wegen der impliziten Template-Auswerte-Regeln.
-
Ich kann dein Problem nicht nachvollziehen?
#include <iostream> #include <vector> #include <cassert> template <typename T> class vector { public: explicit vector(int size) : data(size) {} const T& operator[](int n) const { return data[n]; } T& operator[](int n) { return data[n]; } int num_rows() const { return data.size(); } friend const vector<T> operator+(const vector<T>& lhs, const vector<T>& rhs) { assert(lhs.num_rows() == rhs.num_rows()); vector<T> result(lhs.num_rows()); for (int i=0; i<lhs.num_rows(); i++) { result[i] = lhs[i] + rhs[i]; } return result; } template <typename D> friend const vector<T> operator+(const vector<T>& lhs, const D& rhs) { vector<T> result(lhs.num_rows()); for (int i=0; i<lhs.num_rows(); i++) { result[i] = lhs[i] + rhs; } return result; } template <typename D> friend const vector<T> operator+(const D& lhs, const vector<T>& rhs) { vector<T> result(rhs.num_rows()); for (int i=0; i<rhs.num_rows(); i++) { result[i] = lhs + rhs[i]; } return result; } private: vector(); std::vector<T> data; }; template <typename T> std::ostream& operator<<(std::ostream& s, const vector<T>& v) { s << "[ "; for (int i=0; i<v.num_rows()-1; i++) { s << v[i] << ", "; } s << v[v.num_rows()-1] << "]\n"; return s; } int main (int argc, char * const argv[]) { vector<double> a(3),b(3); a[0] = 5; a[2] = 7; b[1] = 3; std::cout << a << b; std::cout << a+b << a+5 << 5+a << 2.5 + a + 2.5 << a+5.0 << 0. + a + 0L + 5U; return 0; }