Problem mit Mehrfachvererbung in Kombination mit Kovarianz



  • Hallo zusammen
    Wie der Titel bereits aussagt, habe ich ein Problem mit Mehrfachvererbung in Kombination mi Kovarianz, und zwar habe ich folgende Architektur:

    Data Structures  |         Iterators
         ---------------  |       -------------
                          |
         Collection       |              Iterator
          |      |        |              |      |
         \/     \/        |             \/     \/
    Dictionary  Tree      | DictionaryIterator TreeIterator
          |      |        |              |      |
         \/     \/        |             \/     \/
        RedBlackTree      |       RedBlackTreeIterator
    

    Die Collection hat eine Methode GetItems welche einen Iterator als Rückgabetyp hat. Dictionary überschreibt GetItems mit einem DictionaryIterator als Rückgabetyp gemäss Kovarianz. Analog dazu überschreiben auch Tree und RedBlackTree die Methode GetItems der Collection Klasse.

    Beim Versuch, das Ganze zu kompilieren erhalte ich folgenden Fehler:

    1>c:\users\samuel\desktop\projekte\nemesis\development\nemesis\datastructures.h(734) : error C2555: 'RedBlackTree<TK,TI>::GetItems': overriding virtual function return type differs and is not covariant from 'Tree<T>::GetItems'
    1> with
    1> [
    1> TK=uint32,
    1> TI=float32
    1> ]
    1> and
    1> [
    1> T=float32
    1> ]
    1> c:\users\samuel\desktop\projekte\nemesis\development\nemesis\datastructures.h(151) : see declaration of 'Tree<T>::GetItems'
    1> with
    1> [
    1> T=float32
    1> ]
    1> 'Iterator<T>' : ambiguous base is not covariant
    1> with
    1> [
    1> T=float32
    1> ]
    1> c:\users\samuel\desktop\projekte\nemesis\development\nemesis_testing\nemesis_testing.cpp(49) : see reference to class template instantiation 'RedBlackTree<TK,TI>' being compiled
    1> with
    1> [
    1> TK=uint32,
    1> TI=float32
    1> ]
    1>c:\users\samuel\desktop\projekte\nemesis\development\nemesis\datastructures.h(734) : error C2555: 'RedBlackTree<TK,TI>::GetItems': overriding virtual function return type differs and is not covariant from 'Dictionary<TK,TI>::GetItems'
    1> with
    1> [
    1> TK=uint32,
    1> TI=float32
    1> ]
    1> c:\users\samuel\desktop\projekte\nemesis\development\nemesis\datastructures.h(122) : see declaration of 'Dictionary<TK,TI>::GetItems'
    1> with
    1> [
    1> TK=uint32,
    1> TI=float32
    1> ]
    1> 'Iterator<T>' : ambiguous base is not covariant
    1> with
    1> [
    1> T=float32
    1> ]

    Ich komme echt nicht weiter, bringe das Teil einfach nich zu laufen :p Hatte jemand von euch schon mal ein ähnliches Problem und kann mir einen Rat geben?

    Mfg Samuel



  • Das erste was mir jetzt einfiele wäre zwischen dictionary und dem rb-tree sowie zwischen tree und dem rb-tree jeweils eine Zwischenklasse einzubauen, die GetItems so überlädt, dass sie schon einen RBTreeIterator zurückliefert:

    class RBTIreturningTree : public Tree
    {
      RedBlackTreeIterator GetItems();
    };
    
    class RBTIreturningDictionary : public Dictionary
    {
      RedBlackTreeIterator GetItems();
    };
    
    class RedBlackTree : public Tree, public Dictionary
    {
      RedBlackTreeIterator GetItems();
    };
    

    Ach ja, du hast ja dran gedacht, von Collection und Iterator jeweils virtuell zu erben oder?



  • Nein ich habe nicht virtuell vererbt, was IMHO auch nur dann sinnvoll wäre, wenn die Collection, Tree oder Dictionary Membervariablen enthalten würden, was sie nicht tun.

    Folgende Aussage habe ich gefunden:

    Last I checked, visual studios doesn't support covariant return types
    with virtual inheritance and/or multiple inheritance. Testing with VS
    2008, aka v9.
    

    auf der URL http://groups.google.com/group/comp.lang.c++/browse_thread/thread/e16eb9c132dd4cc8/47f210ecd56bec45?lnk=raot&fwc=2


  • Mod

    Die Rückgabe kovarianter Rückgabetypen funktioniert nur, wenn (ausgehend von der ursprünglichen Deklaration der virtuellen Funktion in der Basisklasse) die Konvertierung von der abgeleiteten Klasse in die Basisklasse eindeutig ist und den Zugriffsbeschränkungen genügt (klar) und dabei keine virtuelle Vererbung stattfindet. Damit ist Mehrfachvererbung ausgeschlossen. Der Grund für das Verbot virtueller Basisklasen ist, dass der durch den Compiler verwendete Mechanismus einfach sein soll, ohne signifikanten Overhead zu produzieren, von der Komplexität her also etwa einem static_cast entsprechend sollte. Ein static_cast aus einer virtuellen Basisklasse in eine abgeleitete Klasse ist aber nicht möglich, weil dieser Downcast komplex ist (der vollständige Typ des Objektes muss ermittelt werden). Das ist im Übrigen kein compilerspezifisches Problem sondern Standard C++.

    Das alles muss kein Problem sein, wenn man sich von der Vorstellung löst, dass Polymorphie durch virtuelle Interfacefunktionen bewirkt werden muss.

    struct A
    {
        virtual ~A() {}
        A* foo() { return foo_impl(); }
    private:
        virtual A* foo_impl() { ... }
    };
    struct B : virtual A
    {
        B* foo() { return dynamic_cast<B*>(foo_impl()); }
    private:
        virtual A* foo_impl() { ... }
    };
    struct C : virtual A
    {
        C* foo() { return dynamic_cast<C*>(foo_impl()); }
    private:
        virtual A* foo_impl() { ... }
    };
    struct D : B, C
    {
        D* foo() { return dynamic_cast<D*>(foo_impl()); }
    private:
        virtual A* foo_impl() { ... }
    };
    

    Auf diese Weise lassen sich problemlos alle technischen Einschränkungen, die in C++ mit kovarianten Returntypen ((nur Referenzen und nackte Zeiger) umgehen, man kann sich damit sogar Kontravarianz für Funktionsparameter beschaffen.

    Im konkreten Problem habe ich allerdings gewisse Zweifel, ob diese Vererbungshierarchie, insbesondere bei den Iteratoren, überhaupt sinnvoll ist.


Anmelden zum Antworten