Operatorn überladen - Wann global, wann als Element?



  • Weil der Konstruktor nicht explicit ist, das heisst, dass er, wenn er einen Datentypen findet, der zu einem Konstruktor passt er diesen Konstruktor impliziet aufrufen darf.

    Bruch(int pZahl) {
    nenner = pZahl; zaehler = pZahl;
    }
    

    Sollte wohl eher:

    Bruch(int pZahl) {
    nenner = 1; zaehler = pZahl;
    }
    

    sein..

    Operator = erwartet ja 2 Brüche. Und da er direkt keine findet, probiert er durch den Aufruf des Konstruktors welche zu konstruieren. Was ja geht, da du int als Argument akzeptierst. (spiel ruhig auch mal mit dem explicit Schlüsselwort rum, dann siehst du, was da passiert).



  • Hab gerade mal eine ältere Vektorklasse umgeschrieben und um templates erweitert und wollte dort Operatoren überladen:

    #include "Vektor.h"
    
    int main() {
        Vektor<int> Test(1,2,3);
        std :: cout << Test.GetX() << std :: endl;
        std :: cout << Test.GetY() << std :: endl;
        std :: cout << Test.GetZ() << std :: endl;
    
        Test = Test + 2;
    
        std :: cout << Test.GetX() << std :: endl;
        std :: cout << Test.GetY() << std :: endl;
        std :: cout << Test.GetZ() << std :: endl;
    }
    
    /* Vektor.h */
    class Vektor {
    
        private:
            T x; T y; T z;
    
        public:
            Vektor(T pX, T pY, T pZ);
            Vektor(T pAll);
            Vektor(const Vektor &pVektor);
    
            Vektor& operator=(const Vektor &pVektor);
            Vektor& operator+=(const Vektor &pVektor);
            Vektor& operator-=(const Vektor &pVektor);
            Vektor& operator*=(const Vektor &pVektor);
            Vektor& operator/=(const Vektor &pVektor);
            const Vektor& EinheitsVektor() const;
    
            T GetLen() const;
            T GetX() const;
            T GetY() const;
            T GetZ() const;
    };
    
    template <typename T>
    const Vektor<T> operator+(const Vektor<T> &pVektor1, const Vektor<T> &pVektor2) {
        return Vektor<T>(pVektor1.GetX() + pVektor2.GetX(), pVektor1.GetY() + pVektor2.GetZ(),pVektor1.GetZ() + pVektor1.GetZ());
    }
    

    Error: no match for 'operator+' in 'Test + 2'|



  • Ja, das ist, weil eine Klassentemplate den Typen wissen muss. Also musst du das den Typen explizit angeben.

    Test = Test + Vektor<int>( 2 );
    


  • Aber dies hier geht doch:

    template <typename T>
    const Vektor<T> operator+(T zahl, const Vektor<T> &pVektor) {
    //Variante 1: Performancemäßig besser würde ich sagen
        return Vektor<T>(zahl + pVektor.GetX(), zahl + pVektor.GetZ(),zahl + pVektor.GetZ());
    //Variante 2: Sieht schöner aus
    return Vektor<T>(zahl) + pVektor;
    }
    
    /* Aufruf */
    Test = 2 + Test;
    

    Mal ganz ab davon welche Version nun schöner aussieht/besser ist, müsste ich doch für die Addition(usw) 3 Funktionen schreiben wenn ich das wie oben angegeben Aufrufen möchte oder nicht?



  • /Edit: sry - Thread zu ungenau gelesen



  • Ein kleiner Verweis auf Boost.operator: die als Methoden angelegten Operatoren werden hiermit auch global.



  • Pille456 schrieb:

    Mal ganz ab davon welche Version nun schöner aussieht/besser ist, müsste ich doch für die Addition(usw) 3 Funktionen schreiben wenn ich das wie oben angegeben Aufrufen möchte oder nicht?

    Nein, du hast ja einen nicht expliziten Konstruktor mit einem Parameter vom Typ T . Da sollte es reichen, einen Operator für zwei Vektoren zu definieren, dann wird T implizit in Vektor<T> umgewandelt.

    Dir ist aber schon bewusst, dass die Addition von Vektoren und skalaren Grössen in der Mathematik nicht sinnvoll ist? Auch der Konstruktor mit einem Parameter könnte sehr verwirrend sein. Kommt es denn bei dir so häufig vor, dass ein Vektor mit drei gleichen Komponenten verwendet wird? Ich glaube nicht, und selbst wenn, würde ich keine solche Funktionalität anbieten (zumindest nicht in der Klasse).



  • Nexus schrieb:

    Dir ist aber schon bewusst, dass die Addition von Vektoren und skalaren Grössen in der Mathematik nicht sinnvoll ist?

    Wie kommst Du auf die Annahme?



  • Nexus schrieb:

    Nein, du hast ja einen nicht expliziten Konstruktor mit einem Parameter vom Typ T . Da sollte es reichen, einen Operator für zwei Vektoren zu definieren, dann wird T implizit in Vektor<T> umgewandelt.

    Nun wie schon gesagt, ist dies nicht der Fall:

    class Vektor {
            //...
            template <typename T2>
            Vektor(T2 pAll);
    
    };
    
    template <typename T1, typename T2>
    const Vektor<T1> operator+(const Vektor<T1> &pVektor1, const Vektor<T2> &pVektor2) {
        return Vektor<T1>(pVektor1.GetX() + pVektor2.GetX(), pVektor1.GetY() + pVektor2.GetY(),pVektor1.GetZ() + pVektor2.GetZ());
    }
    
    int main() {
        Vektor<float> Test(1.2,2,3);
    
        std :: cout << Test.GetX() << std :: endl;
        std :: cout << Test.GetY() << std :: endl;
        std :: cout << Test.GetZ() << std :: endl;
    
        Test += 2;
    
        std :: cout << Test.GetX() << std :: endl;
        std :: cout << Test.GetY() << std :: endl;
        std :: cout << Test.GetZ() << std :: endl;
    }
    

    error: no match for 'operator+' in 'Test + 2'



  • im Josuttis/Vandervoorde steht in nem Nebensatz was von

    C++ Templates - The Complete Guide, S.16 schrieb:

    Because automatic type conversion is not considered for templates...

    Das erklärt vermutlich warum er den op+ nicht findet...



  • Also für den Fall dass ich templates verwende muss ich doch 3 Funktionen schreiben? (Also für den Fall "Vektor + Vektor", "Zahl + Vektor" und "Vektor + Zahl")



  • müsstest du, ja.
    Ich würde mir allerdings eher Gedanken darüber machen, welchen Sinn ein Konstruktor mit nur einem Argument für einen Vektor hat, genauso darüber, welchen Sinn eine Addition eines Vektors mit einem Skalar haben könnte. Mathematisch betrachtet ist nämlich beides ziemlicher Humbug.

    /edit: Standard-Auszug zum argument-deduction-Problem:

    14.8.2.1 Deducing template arguments from a function call [temp.deduct.call] schrieb:

    1 Template argument deduction is done by comparing each function template parameter type (call it P) with the type of the corresponding argument of the call (call it A) as described below.

    2 If P is not a reference type:

    If A is an array type, the pointer type produced by the array-to-pointer standard conversion (4.2) is used in place of A for type deduction; otherwise,
    If A is a function type, the pointer type produced by the function-to-pointer standard conversion (4.3) is used in place of A for type deduction; otherwise,
    If A is a cv-qualified type, the top level cv-qualifiers of A's type are ignored for type deduction.
    If P is a cv-qualified type, the top level cv-qualifiers of P's type are ignored for type deduction. If P is a reference type, the type referred to by P is used for type deduction.
    3 In general, the deduction process attempts to find template argument values that will make the deduced A identical to A (after the type A is transformed as described above). However, there are three cases that allow a difference:

    If the original P is a reference type, the deduced A (i.e., the type referred to by the reference) can be more cv-qualified than A.
    A can be another pointer or pointer to member type that can be converted to the deduced A via a qualifi cation conversion (4.4).
    If P is a class, and P has the form template-id, then A can be a derived class of the deduced A. Like wise, if P is a pointer to a class of the form template-id, A can be a pointer to a derived class pointed to by the deduced A.
    These alternatives are considered only if type deduction would otherwise fail. If they yield more than one possible deduced A, the type deduction fails. [Note: if a template-parameter is not used in any of the function parameters of a function template, or is used only in a non-deduced context, its corresponding template-argument cannot be deduced from a function call and the template-argument must be explicitly specified. ]

    Implizite Konvertierungen jenseits der aufgeführten werden eben nicht in Betracht gezogen.



  • Tachyon schrieb:

    Wie kommst Du auf die Annahme?

    Ein Vektorraum definiert unter seinen Elementen nur die Operationen Addition und Multiplikation mit einem Skalar. Klar sind Verknüfpungen wie beispielsweise Skalar- und Vektorprodukt möglich, diese haben aber auch eine Verwendung in der Mathematik.

    Wie kommst du denn auf die Gegenannahme?

    pumuckl schrieb:

    Ich würde mir allerdings eher Gedanken darüber machen, welchen Sinn ein Konstruktor mit nur einem Argument für einen Vektor hat, genauso darüber, welchen Sinn eine Addition eines Vektors mit einem Skalar haben könnte. Mathematisch betrachtet ist nämlich beides ziemlicher Humbug.

    👍



  • Nexus schrieb:

    Dir ist aber schon bewusst, dass die Addition von Vektoren und skalaren Grössen in der Mathematik nicht sinnvoll ist?

    Nexus schrieb:

    Ein Vektorraum definiert unter seinen Elementen nur die Operationen Addition und Multiplikation mit einem Skalar.[...]

    😕



  • Tachyon schrieb:

    😕

    Drück dich bitte mal anständig aus. Man kann so ja nur erraten, worauf du hinauswillst.

    Auch deine Fragen ("Wie kommst du auf diese Annahme?") dürften ruhig etwas aussagekräftiger formuliert sein. Beispielsweise könntest du erwähnen, was für dich nicht ins Konzept passt.

    Edit: Ich habe möglicherweise deine Anspielung erraten:
    Ich habe mich ein bisschen unklar ausgedrückt.

    • Mit "Addition von Vektoren und skalaren Grössen" im ersten Teil meinte ich Addition, bei der ein Operand ein Vektor und einer ein Skalar ist.
    • "Addition und Multiplikation mit einem Skalar" (zweiter Teil) bedeutet Addition mit zwei Vektoren als Operanden beziehungsweise Multiplikation eines Vektors mit einem Skalar.

    Wenn es das war, sorry für das Missverständnis, es war wirklich zweideutig formuliert. Oben genannte Kritik an deinen Fragestellungen gilt natürlich weiterhin. 😉


Anmelden zum Antworten