Identische virtuelle Funktion aus zwei Basisklassen, kann man nur eine davon überschreiben?



  • Ich habe einen Fall wo eine Klasse X die selbe virtuelle Funktion (selber Name+Signatur) von zwei Basisklassen erbt. Eine der beiden soll nun überschrieben werden, die andere aber nicht.

    Geht das irgendwie?

    Also konkret möchte ich das machen:

    struct A {
        virtual void foo() = 0;
    };
    
    struct AImpl : A {
        virtual void foo() final {}; // dieses final ist einigermassen wichtig
    };
    
    struct B {
        virtual void foo() = 0;
    };
    
    struct X : AImpl, B {
        void B::foo() final { AImpl::foo(); }
    };
    
    void test() {
        X x;
        x.foo();
    }
    

    MSVC frisst das auch in genau dieser Form. Die Frage ist: geht das irgendwie mit Standard C++? Ich weiss dass ich eine Zwischenklasse verwenden kann ala

    struct A {
        virtual void foo() = 0;
    };
    
    struct AImpl : A {
        virtual void foo() final {};
    };
    
    struct B {
        virtual void foo() = 0;
    };
    
    struct BX : B {
        virtual void foo() final {
            fooX();
        }
    
        virtual void fooX() = 0;
    };
    
    struct X : AImpl, BX {
        void fooX() final { AImpl::foo(); }
        using AImpl::foo;
    };
    
    void test() {
        X x;
        x.foo();
    }
    

    Aber das ist halt IMO ziemlich hässlich und unnötiges Rauschen.



  • @hustbaer sagte in Identische virtuelle Funktion aus zwei Basisklassen, kann man nur eine davon überschreiben?:

    Die Frage ist: geht das irgendwie mit Standard C++?

    Mir fällt dazu keine Lösung ein.

    Musst du von AImpl ableiten, oder kann es auch ein Member sein?

    Welche MSVC Version lässt das zu? /permissive:- ? Nach welchem Standard?



  • @hustbaer sagte in Identische virtuelle Funktion aus zwei Basisklassen, kann man nur eine davon überschreiben?:

    Aber das ist halt IMO ziemlich hässlich und unnötiges Rauschen.

    Könnte das hier vielleicht mit etwas weniger Rauschen das tun, was du erreichen willst?

    struct AImplBase : A {
        virtual void foo() {};
    };
    
    struct AImpl : AImplBase {
        virtual void foo() final { foo(); }; // dieses final ist einigermassen wichtig
    };
    
    ...
    
    struct X : AImplBase, B {
        void foo() final { AImplBase::foo(); }
    };
    

    AImplBase halt für die Fälle, wo man foo doch noch überschreiben will.



  • Gerade im Stroustrup nachgelesen (C++11 §21.3.5), es müssen alle foos überschrieben werden. D.h. entweder in AImpl final nutzen, oder in X foo überschreiben.



  • @manni66 Ja, muss ableiten. Welche MSVC Versionen weiss ich nicht genau. Ging schon bei 2005 und geht bei 2019 noch. Und ja, geht auch mit /permissive-. Ich nehme an dass MS das eingebaut hat damit es einfacher wird COM Objekte zu implementieren die verschiedene Interfaces implementieren wo gleichnamige Funktionen mit unterschiedlicher Bedeutung vorkommen.



  • @Finnegan
    Nö, ist keine Lösung. AImpl ist die gemeinsame Basisklasse von der abgeleitet wird und in dieser sollte die Methode final sein.

    "Doch noch überschreiben" ist ja gerade unerwünscht weil es überhaupt keinen Sinn macht. Die Methode ist bloss virtuell weil A eine reine Interface-Klasse ist, nicht weil man die Methode in abgeleiteten Klassen überschreiben können sollte.



  • @hustbaer sagte in Identische virtuelle Funktion aus zwei Basisklassen, kann man nur eine davon überschreiben?:

    @Finnegan
    Nö, ist keine Lösung. AImpl ist die gemeinsame Basisklasse von der abgeleitet wird und in dieser sollte die Methode final sein.

    Ja verstehe. Ich dachte es wäre lediglich wichtig, dass die Funktion "in der Basisklasse, von der abgeleitet wird" final ist. Das wäre bei mir mit AImpl immer noch der Fall, allerdings nicht mehr als "gemeinsame Basisklasse". Das wäre dann AImplBase ohne final.

    "Doch noch überschreiben" ist ja gerade unerwünscht weil es überhaupt keinen Sinn macht. Die Methode ist bloss virtuell weil A eine reine Interface-Klasse ist, nicht weil man die Methode in abgeleiteten Klassen überschreiben können sollte.

    Mit "überschreiben" meine ich auch das Implementieren abstrakter Funktionen. Nach meinem Verständnis, wie Standard-C++ das hier interpretiert (ohne den in Zeile 14 qualifizierten Namespace B::foo), werden hier in X zwei eigentlich individuelle Member-Funktionen gleichzeitig überschieben, auch wenn sie keinen gemeinsamen Ursprung haben: AImpl::foo und B::foo (abstrakt). Erstere schlägt wegen dem final fehl. Für mich ein Fall von "doch noch überschreiben".



  • Also ich bin mir nicht sicher, aber vielleicht mit enable_if. Dann wenn von den Basisklassen abgeleitet wird.

    struct B : public A1<true>, public A2<false>
    

    oder so etwas.



  • @Finnegan sagte in Identische virtuelle Funktion aus zwei Basisklassen, kann man nur eine davon überschreiben?:

    Nach meinem Verständnis, wie Standard-C++ das hier interpretiert (ohne den in Zeile 14 qualifizierten Namespace B::foo), werden hier in X zwei eigentlich individuelle Member-Funktionen gleichzeitig überschieben, auch wenn sie keinen gemeinsamen Ursprung haben: AImpl::foo und B::foo (abstrakt). Erstere schlägt wegen dem final fehl. Für mich ein Fall von "doch noch überschreiben".

    Ja, klar, das sind die Regeln. Ich wollte wissen ob man die irgendwie aushebeln kann, so dass eben nur B::foo überschrieben wird. Weil A::foo ja schon final ist.

    Ich kenne keine direkte Möglichkeit in Standard C++, ihr auch nicht, also wird's wohl nicht gehen. Wollte nur sicher gehen - ich weiss ja auch lange nicht alles über C++ - speziell aktuellere Standards.



  • @titan99_ sagte in Identische virtuelle Funktion aus zwei Basisklassen, kann man nur eine davon überschreiben?:

    Also ich bin mir nicht sicher, aber vielleicht mit enable_if. Dann wenn von den Basisklassen abgeleitet wird.

    struct B : public A1<true>, public A2<false>
    

    oder so etwas.

    Verstehe ich ehrlich gesagt nicht. Ich habe zwei reine Interface-Klassen, A und B. Zu A gibt es eine Basisklasse (AImpl in meinem Beispiel) die für quasi alles Default-Implementierungen hat, wovon viele final sein sollen, weil es Quatsch wäre sie zu überschreiben (und solcher Quatsch-Code existiert und gefunden wurde und wir neuen Quatsch-Code vermeiden möchten).

    A und B haben jeweils die Funktion foo - und in beiden wird sie benötigt (es gibt Komponenten die mit A arbeiten und foo brauchen und welche die mit B arbeiten und foo brauchen).

    Wenn ich dich richtig verstehe meinst du ich sollte ein Interface BWithoutFoo machen (A2<false>). Klar, das ginge dann. Nur ... mit was befüttere ich dann die Komponenten die mit B arbeiten? Die können mit BWithoutFoo nixe anfangen.


Log in to reply