Virtuelle Funktionen mit spezifischen Parametern



  • Hallo,

    um in einer abgeleiteten Klasse eine virtuelle Funktion zu überschreiben, muss sie die selbe Signatur wie in der Basisklasse haben. Leider brauche ich aber als Parameter einen Typ der abgeleiteten Klasse.

    Hintergrund: ich habe folgende Basisklasse:

    struct Eq {
        // minimal definition: == or !=
    
        virtual bool operator ==(Eq ref) const { return !(*this != ref); }
    
        virtual bool operator !=(Eq ref) const { return !(*this == ref); }
    };
    

    Haskell-Programmierer werden das wiedererkennen. Man beachte, dass es sinnfrei ist, Eq zu instanzieren, denn die Operatoren nehmen gegenseitigen Bezug, d.h. man sitzt in einer Endlosschleife.

    Nun möchte ich diese Basisklasse implementieren. Ich habe als (sinnfreie) Beispielklasse 'Int' gewählt:

    class Int : public Eq {
    private:
        int val;
    
    public:
        Int() : val(0) {}
        Int(int _val) : val(_val) {}
    
        bool operator ==(Eq ref) const
        {
            std::cerr << "Int::operator ==(Eq)" << std::endl;
            return true; // tja, was tun?
        }
    
        inline Int& operator =(Int ref)
        {
            this->val = ref.val;
            return *this;
        }
    
        operator int() const { return this->val; }
    };
    

    Ich möchte natürlich nicht beide Operatoren, == und != neu definieren, wozu auch? Die Standard-Definition in der Basisklasse macht das überflüssig, es reicht, einen der Operatoren zu definieren.

    Problem im obigen Design: ich will ja eigentlich gar keinen Vergleich zwischen Int und Eq definieren, sondern einen zwischen Int und Int. Wie kann ich vom Compiler her sicherstellen, dass Int::operator== nur ein Int übergeben werden kann? Und wie kann ich auf 'ref.val' zugreifen? Denn 'val' ist ja für Eq nicht definiert.

    Ich kann natürlich einen Operator Int::operator==(Int) definieren -- aber dann überschreibt dieser nicht die Basisfunktion, was bedeutet, dass Eq::operator!= nicht auf ihn Bezug nimmt und wenn man Int::operator!= aufruft, kommt es zu einer Endlosschleife.

    Uh, ich hoffe, da steigt jemand durch. Was ich möchte, ist im Prinzip eine Haskell-artige Klassenstruktur.



  • Mir würde auch ein "geht nicht" als Antwort reichen. Dann motte ich den C++-Compiler ein und verwende nur noch Haskell. 😉



  • Du kannst nicht Haskell in C++ Programmieren.

    Man definiert operatoren idR nicht als virtuell.

    Du musst es schon so machen, wie man es in C++ macht (was auch immer du machen willst, in C++ fuehlt es sich falsch an).



  • Konrad Rudolph schrieb:

    Mir würde auch ein "geht nicht" als Antwort reichen. Dann motte ich den C++-Compiler ein und verwende nur noch Haskell. 😉

    "Geht nicht" gibt's bei C++ sehr selten 😉 !

    Ich kann natürlich einen Operator Int::operator==(Int) definieren -- aber dann überschreibt dieser nicht die Basisfunktion, was bedeutet, dass Eq::operator!= nicht auf ihn Bezug nimmt und wenn man Int::operator!= aufruft, kommt es zu einer Endlosschleife.

    Wenn Sie sichergehen wollen, daß in einer Basisklasse von Eq eine virtuelle Funktion nicht mehr aufgerufen werden kann, deklarieren sie diese einfach als private (oder protected):

    class Int : public Eq {
    private:
        int val;
    
    public:
        Int() : val(0) {}
        Int(int _val) : val(_val) {}
    
        inline Int& operator =(Int ref)
        {
            this->val = ref.val;
            return *this;
        }
    
        operator int() const { return this->val; }
    
    private:
        virtual bool operator == (Eq ref);
    };
    

    Allerdings ist es ziemlich sinnlos, von einer Basisklasse virtuelle Funktionen zu erben, die dann nicht benutzt werden sollen...

    Moritz

    *keinen-sinn-drinn-seh*



  • Shade Of Mine schrieb:

    Du kannst nicht Haskell in C++ Programmieren.

    Man definiert operatoren idR nicht als virtuell.

    Das mit den Operatoren war auch nur ein Beispiel, weil's so schön einfach ist. Es geht prinzipiell darum, dass eine vererbte Funktion einen Parameter hat, der den Typ der Klasse hat -- und zwar den Typ der vererbten Klasse.

    Du musst es schon so machen, wie man es in C++ macht (was auch immer du machen willst, in C++ fuehlt es sich falsch an).

    Was ich da versuche, hat ehrlich gesagt auch wenig mit "konventioneller" oder "produktiver" Programmierung in C++ zu tun; es geht mir vielmehr darum, Klassenkonzepte auszuprobieren -- und zwar nicht unbedingt die von C++ selbst.



  • Konrad Rudolph schrieb:

    Es geht prinzipiell darum, dass eine vererbte Funktion einen Parameter hat, der den Typ der Klasse hat -- und zwar den Typ der vererbten Klasse.

    C++ erlaubt beim Übeschreiben kovariante Typen nur beim Rückgabewert. Nicht aber bei Parametern.

    Wenn du dein Ziel genauer beschreibst, können wir dir aber sicher einen Implementationsvorschlag geben.



  • HumeSikkins schrieb:

    Wenn du dein Ziel genauer beschreibst, können wir dir aber sicher einen Implementationsvorschlag geben.

    In diesem Fall ist der Weg wirklich das Ziel. D.h. ich wollte genau das erreichen, um das Klassenkonzept von Haskell in C++ zu implementieren.



  • Konrad Rudolph schrieb:

    In diesem Fall ist der Weg wirklich das Ziel. D.h. ich wollte genau das erreichen, um das Klassenkonzept von Haskell in C++ zu implementieren.

    Was natürlich Blödsinn ist.

    In C++ macht man Sachen eben anders als in Haskell... Da ist eine 1:1 Umsetzung nicht sinnvoll.



  • Shade Of Mine schrieb:

    Konrad Rudolph schrieb:

    In diesem Fall ist der Weg wirklich das Ziel. D.h. ich wollte genau das erreichen, um das Klassenkonzept von Haskell in C++ zu implementieren.

    Was natürlich Blödsinn ist.

    Ich sagte doch: das ganze war experimentell. Wenn es darum ginge, produktiven C++-Code zu schreiben, *wäre* das ganze natürlich Blödsinn ...



  • Nur fürs Protokoll: Ich bin plöt, es geht natürlich doch:

    template <class T> struct Eq {
        // minimal definition: == or !=
    
        virtual bool operator ==(T ref) const { return !(*this != ref); }
    
        virtual bool operator !=(T ref) const { return !(*this == ref); }
    };
    
    class Int : public Eq<Int> {
    private:
        int val;
    
    public:
        Int() : val(0) {}
        Int(int _val) : val(_val) {}
    
        bool operator ==(Int ref) const { return this->val == ref.val; }
    
        inline Int& operator =(Int ref)
        {
            this->val = ref.val;
            return *this;
        }
    
        operator int() const { return this->val; }
    };
    


  • Naja, was bringt das, wenn unterschiedliche Eq-Subklassen eigentlich von verschiedenen Eq-Instantiierungen abgeleitet sind?



  • Bashar schrieb:

    Naja, was bringt das, wenn unterschiedliche Eq-Subklassen eigentlich von verschiedenen Eq-Instantiierungen abgeleitet sind?

    Typen-Constraints für Generizität. 😉

    Ob das in C++ etwas bringt, weiß ich nicht. Ich wollte eben auch nur ausprobieren, wie man das Klassenkonzept von Haskell ohne low-level-Speicherzugriff implementieren kann. Haskell ist eben extrem high-level, daher sind Implementierungsdetails gut versteckt und ich versuche, das ein wenig aufzuschlüsseln.



  • Konrad Rudolph schrieb:

    Typen-Constraints für Generizität. 😉

    In C++ werden Constraints idR durch die Schnittstelle und nicht durch die Elternklassen definiert.

    dh, du erstellst einfach ein Concept mit der bedingung dass der op== unterstuetzt werden muss und boost macht den rest 🙂


Anmelden zum Antworten