Überschreiben rein virtueller Funktionen durch Verwendung einer separat geerbten Methode

8

Nur ein kleiner Ärger, da ich das Problem umgehen kann, indem ich die abgeleitete Funktion umschließe, anstatt das Schlüsselwort 'using' zu verwenden, aber warum funktioniert das nicht (der Compiler sagt mir, dass 'get_elem' immer noch rein virtuell ist) "Bar" -Klasse).

%Vor%

Prost,

Tom

    
Tom 19.08.2010, 02:14
quelle

3 Antworten

5

Wenn Goo ein "Mixin" ist, das entwickelt wurde, um die Schnittstelle Foo in einer bestimmten Weise zu implementieren (es könnte andere Mixins mit anderen Implementierungen geben), dann kann Goo von Foo abgeleitet werden (anstatt Bar dies zu tun).

Wenn Goo nicht dazu gedacht ist, die Schnittstelle Foo zu implementieren, dann wäre es ein schrecklicher Fehler, Bar so zu behandeln, als hätte es diese rein virtuelle Funktion implementiert, obwohl es zufällig eine Funktion der gleichen Signatur hat. Wenn Sie implizite Schnittstellen und "Duck" in C ++ eingeben möchten, können Sie dies tun, aber Sie müssen es mit Vorlagen tun. Richtig oder falsch, rein virtuelle Funktionen sind für explizit deklarierte Interfaces, und Goo's get_elem Funktion ist nicht explizit deklariert um Foo::get_elem zu implementieren. So ist es nicht.

Ich denke, das erklärt nicht, warum die Sprache im Prinzip nicht using Goo::get_elem for Foo; oder eine solche Deklaration in Bar definieren konnte, um zu vermeiden, dass Bar eine Menge Vorform enthält, die den Anruf umschließt.

Sie können vielleicht etwas mit Templates machen, um Goo in gewissem Umfang zu unterstützen, ohne wirklich über Foo zu wissen:

%Vor%

Wo Fuu ist eine andere Schnittstelle, die eine get_elem Funktion hat. Offensichtlich liegt es dann in der Verantwortung des Autors von Bar sicherzustellen, dass Goo wirklich den Vertrag von Foo implementiert, und dasselbe für Baz den Vertrag von Fuu prüft.

Übrigens ist diese Form der Kovarianz ein bisschen zweifelhaft. Wenn man Foo betrachtet, könnte jemand erwarten, dass der Ausdruck bar.get_elem() = Elem() gültig ist, und das ist es auch nicht, also wird LSP verletzt. Referenzen sind so lustig. ((Foo &)bar).get_elem() = Elem() ist gültig, funktioniert aber im Allgemeinen nicht! Es wird nur dem Unterobjekt Elem zugeordnet, und in diesem Fall auch ((Foo &)bar).get_elem() = DerivedElem() . Die polymorphe Zuweisung ist grundsätzlich ein Ärgernis.

    
Steve Jessop 19.08.2010 02:26
quelle
2

In Ihrem Beispiel sind Foo und Goo separate Klassen. In Bar ist die Methode get_elem von Goo mit der in Foo nicht identisch, auch wenn ihre Signatur übereinstimmt.

Wenn Sie using Goo::get_elem haben, sagen Sie dem Compiler einfach, den unqualifizierten Aufruf von get_elem () auf den in Goo zu setzen.

    
RichN 19.08.2010 02:20
quelle
1

Sie haben eine der vielen Ecken von C ++ kennengelernt. In diesem Fall betrachtet C ++ zwei virtuelle Funktionen, die von verschiedenen Klassen übernommen wurden, nicht als dieselbe Funktion, obwohl sie denselben Namen und dieselbe Signatur haben.

Es gibt einige gute Gründe dafür, dass C ++ so handelt. Zum Beispiel ist es häufig der Fall, dass diese beiden Funktionen nicht identisch sind, obwohl sie denselben Namen und dieselbe Signatur haben. Die semantische Bedeutung der beiden Funktionen ist unterschiedlich.

Hier ist ein Beispiel:

%Vor%

Und dann versuchst du das:

%Vor%

Sie müssen also darauf zurückgreifen:

%Vor%

Also hat C ++ einen guten Grund, virtuelle Funktionen mit der gleichen Typsignatur zu behandeln (der Rückgabetyp ist nicht Teil der Typsignatur) und benennt zwei unterschiedliche Basen als unterschiedliche Funktionen.

So weit wie using geht ... Alle eine using Direktive sagt "Fügen Sie die Namen aus diesem anderen Namespace zu diesem Namespace hinzu, als ob sie hier deklariert wären." Dies ist ein Null-Konzept, soweit es virtuelle Funktionen betrifft. Es schlägt lediglich vor, dass jede Mehrdeutigkeit bei der Verwendung eines Namens anders gelöst werden sollte. Er deklariert nur einen Namen, er definiert nicht den Namen. Damit eine virtuelle Funktion überschrieben wird, ist eine neue Definition erforderlich.

OTOH, wenn du eine einfache Thunk-Neudefinition inline eingibst:

%Vor%

Ein guter Compiler sollte das sehen und wissen, dass er sich nicht einmal die Mühe macht, die Funktion zu erstellen, und stattdessen einfach die virtuellen Tabelleneinträge manipulieren, um das Richtige zu tun. Es muss möglicherweise tatsächlich Code für es ausgeben und das Symbol verfügbar haben, falls seine Adresse genommen wird, aber es sollte immer noch in der Lage sein, die virtuelle Tabelle einfach so zu manipulieren, dass die Funktion vollständig verschwindet, wenn sie durch Foo * aufgerufen wird.

    
Omnifarious 19.08.2010 03:06
quelle

Tags und Links