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 Member operator*= 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 Feld eine 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 Member operator*= 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 Feld eine 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;
    }
    

Anmelden zum Antworten