Fehler mit Template-Klasse und globalem Operator



  • JensE schrieb:

    Wie meinst du das? Ich kann doch kein const hinter ne friend-Funktion schreiben.

    stimmt. hab voll unfug geschrieben.



  • Mhhh...

    lass mal das "template <class num_t>" über deiner
    friend-deklaration innerhalb deiner Klasse weg.

    Vielleicht bringt es was und eleminiert die uneindeutigkeit
    deines num_ts.



  • Im Code, in dem ich den Operator verwendet hab, hab ich z.B. geschrieben:

    2 * Vect1
    

    Nun ist Vect1 aber vom Typ Vector4<float>. Wenn man nun 2.0f schreibt, ist man ja wieder davon abhängig, dass num_t auch wirklich float ist, also schätze ich mal, man schreibt am besten

    num_t(2) * Vect1
    

    Was auch funktioniert. Oder gibt es eine bessere Möglichkeit, so dass ich ganz zwanglos

    2 * Vect1
    

    schreiben kann? Ich nehme mal an, man muss dem Operator dann zwei Template-Argumente mitgeben, einen für den Vektortyp, den anderen für den Skalar-Typ. Macht das Sinn, habt ihr das in euren Vektor-Klassen genauso?



  • Mathematisch würde es schon "Sinn" machen, aber ob
    du es später dann noch brauchst ist eine andere Geschichte.
    Falls das eine Mathebibliothek für ein 3d-Spiel werden
    soll ist das ganze sowieso für Fließpunktzahlen
    gedacht und du kannst es direkt in den Quellcode schreiben.

    Falls ein tieferer mathematischer Hintergrund dahintersteckt,
    vielleicht ein Vektorraum über den complexen Zahlen
    mit den reelen Zahlen als Skalare, dann solltest
    du 2 template-Parameter verwenden.

    EDIT: imaginär in complex geändert



  • JensE schrieb:

    Ich habe folgende Template-Klasse:

    template <class num_t>
    class Vector4
    {
    	public:
    
             // ....
    
                      // hier der globale Operator, der Probleme bereitet
    		template <class num_t>
    		friend Vector4<num_t> operator * (num_t fValue, const Vector4<num_t>&);
    
             // ....
    
    };
    

    Illegal, da du zweimal den Template-Parameter num_t deklarierst. Das ist im selben Scope nicht erlaubt.
    Versuch's mal so:

    template <class num_t>
    class Vector4
    {
        public:
            template <class num, class nt>
            friend Vector4<nt> operator * (num fValue, const Vector4<nt>&);
    
    };
    
    template <class num , class nt>
    Vector4<nt> operator * (num fValue, const Vector4<nt>& v) {...}
    

    Oder einfacher:

    template <class num_t>
    class Vector4
    {
        public:
            template <class num>
            friend Vector4<num_t> operator * (num fValue, const Vector4<num_t>& v)
            {
                ...
            }
    
        private:
    };
    


  • @HumeSikkins: Werde ich sofort mal ausprobieren. Ich wundere mich ja auch, dass der Compiler meine Vorgehensweise akzeptiert hat, wo doch beide Template Typen nichts miteinander zu tun haben.

    @Beowulf:

    Du hast schon recht, aber wenn ich erst sowas schreiben muss wie

    num_t(2) * Vect
    

    statt

    2 * Vect
    

    dann brauch ich auch keine Operatoren mehr, da es noch unleserlicher ist, als wenn ich eine Methode namens Scale() verwende.

    Ich frage mich außerdem immernoch, warum der Compiler beim Klassen-Operator * nicht meckert (also wenn ich den Skalar rechts ran multipliziere). Da erkennt er plötzlich keine Mehrdeutigkeiten mehr.

    Ich bevorzuge jedenfalls die Lösung mit nem zweiten Template-Argument, weil die einfach leserlicher ist:

    template <class num_t, class val_t>
    inline Vector4<num_t> operator * (val_t fValue, const Vector4<num_t> &v)
    {
    	return Vector4<num_t>(v.x * fValue, v.y * fValue, v.z * fValue, v.w * fValue);
    }
    

    Vielen Dank, Jens



  • JensE schrieb:

    Um das Ganze noch etwas zu ergänzen:

    (1) da ist noch ein Compile-Fehler, den ich vergessen hab zu posten:

    error C2782: 'LMath::Vector4<num_t> LMath::operator *(num_t,const LMath::Vector4<num_t> &)': Vorlagenparameter 'num_t' ist mehrdeutig
    

    Der Fehler sollte doch klar sein. Du hast deinen Op* mit einem Template-Parameter deklariert (num_t). Wenn du jetzt 2 * Vector4<float> schreibst, dann versucht der Compiler num_t herzuleiten.

    Für den ersten Parameter: 2 hat den Typ int. Also hat num_t den Typ int.
    Für den zweiten Parameter: Hier hat num_t den Typ float.
    Da der selbe Parameter nicht gleichzeitig zwei Typen haben kann, nämlich float und int, und der Compiler keine Münze hat, die er werfen könnte, sagt er dir, dass der Aufruf mehrdeutig ist.



  • Nein das ist eigentlich nicht klar, denn dieselbe Argumentation könnte man dann ja auch bei dem Klassenoperator * bringen. Wenn ich

    Vect * 2
    

    schreibe, kommt kein Mehrdeutigkeitsfehler.

    Mir ist auch noch nicht ganz klar, wie es nun mit den Namen für Template-Typargumente aussieht. Wenn ich ne Template-Klasse habe und darin Template-Methoden, welche den Typnamen der Klasse überschreiben, sollte dann nicht ein Compile-Fehler die Folge sein? Ist aber nicht der Fall, auch bei friend nicht, die Template-Funktionen sind.



  • JensE schrieb:

    Wenn ich ne Template-Klasse habe und darin Template-Methoden, welche den Typnamen der Klasse überschreiben, sollte dann nicht ein Compile-Fehler die Folge sein?

    Exakt. Und die meisten modernen Compiler bringen hier auch einen Fehler. Der gcc z.B. sagt was in der Art: "Parameter XYZ shadows template parameter XYZ".

    Der VC hat diesbezüglich aber eine nicht-konforme Erweiterung. Hier verdeckt zwar der äußere Parameter den inneren, gleichzeitig bleibt der innere Parameter aber für den Compiler herleitbar.
    Beispiel:

    template <class P>
    struct Foo {
    template <class P>
    void bar(P p) {
        cout << typeid(P).name() << endl;
    }
    };
    int main()
    {
    Foo<int> f;
    f.bar(2.0); 
    }
    

    Der VC erlaubt es zwar, dass Foo::P und bar::P unterschiedliche Typen haben. bar::P wird aber von Foo::P verdeckt und du hast in bar keine Möglichkeit auf bar::P zuzugreifen. Das erkennst du daran, dass das Beispiel "int" ausgiebt (also den Typ von Foo::P) statt "double (dem Typ von Bar::P).

    Legal ist das Beispiel aber nicht.



  • Danke für die Aufklärung.

    Die eine Sache von oben mit dem globalen Operator ist mir aber eigentlich doch noch nicht klar, denn dieselbe Argumentation könnte man dann ja auch bei dem Klassenoperator * bringen. Wenn ich

    Vect * 2
    

    schreibe, kommt kein Mehrdeutigkeitsfehler.

    Hier müsste der Compiler doch auch sehen: Aha, ein Operator der Klasse Vector<float> wird verwendet, oh, der zweite Parameter hat aber nicht den Typ float, sondern int.



  • JensE schrieb:

    Die eine Sache von oben mit dem globalen Operator ist mir aber eigentlich doch noch nicht klar, denn dieselbe Argumentation könnte man dann ja auch bei dem Klassenoperator * bringen. Wenn ich

    Vect * 2
    

    schreibe, kommt kein Mehrdeutigkeitsfehler.

    Ich gehe mal davon aus, dass die Deklaration des op* so aussieht:

    template <class num_t>
    class Vector4 {
    public:
        Vector4 operator*(num_t num);
    };
    

    Richtig?

    Hier müsste der Compiler doch auch sehen: Aha, ein Operator der Klasse Vector<float> wird verwendet, oh, der zweite Parameter hat aber nicht den Typ float, sondern int.

    Das ist kein Problem, denn in diesem Fall handelt es sich nicht um ein Template. Es findet demzufolge auch keine Herleitung von Parameter-Typen statt die mehrdeutig sein könnte.

    Nach der Instanziierung deines Vektors hat der Operator* die Signatur:

    Vector4<float> Vector4<float>::operator*(float n);

    Der Parameter hat also einen festen Typ, nämlich float. Sein Typ ist nicht das Ergebnis der Herleitung von Template-Parametern beim Aufruf.

    Beim Aufruf Vec * 2 ist das Argument vom Typ int. Da sich int aber nun implizit nach float konvertieren lässt, kann der Op problemlos aufgerufen werden.



  • Natürlich! Ist ja logisch, es handelt sich ja um einen einzigen Typ: Den der Template-Klasse.

    Danke vielmals.


Anmelden zum Antworten