Das Aufrufen virtueller Memberfunktionen einer Klasse mit einem Zeiger auf die Basisklasse ist in C ++ natürlich sehr üblich. Daher finde ich es seltsam, dass es unmöglich scheint, dasselbe zu tun, wenn man einen Elementzeiger anstelle eines normalen Zeigers hat. Bitte beachten Sie den folgenden Code:
%Vor%Visual C ++ fährt fort zu sagen:
error C2440: 'initializing' : cannot convert from 'D E::* ' to 'B E::* '
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
Ich verstehe nicht, warum diese "nicht verwandt" sind. Warum ist das nicht möglich?
Bearbeiten:
Ich versuche, dies eine C ++ - Frage zu halten und nicht über das spezielle Problem, das ich zu lösen versuche, aber das ist im Wesentlichen, was ich tun möchte:
E ist eine Klasse, die mehrere von B abgeleitete Elemente enthält. Der Vektor v wird verwendet, um Zeiger auf diese Elemente zu organisieren (z. B. umzuordnen). Vektor v ändert sich selten. Mit der Funktion g () können Sie eines der Elemente von E unter Verwendung eines Indexes in v auswählen. Es wird sehr oft und jedes Mal mit einem anderen E aufgerufen.
Wenn Sie darüber nachdenken, ist v nur eine Nachschlagetabelle mit Offsets. Die Funktion g () wählt einfach einen dieser Offsets aus und addiert ihn zum E *, um das B * zurückzugeben. Die Funktion g () wird vom Compiler eingebunden und kompiliert zu nur 4 CPU-Anweisungen, was genau das ist, was ich will:
%Vor%Ich kann mir keinen Grund vorstellen, warum der Standard es nicht erlauben würde, ein D E :: * in ein B E :: * umzuwandeln.
Die einfache Antwort ist, dass C ++ nicht die Konvertierung definiert, die Sie versuchen, und daher ist Ihr Programm schlecht gebildet.
Betrachten Sie Standardkonvertierungen (C ++ 11§4 / 1):
Standardkonvertierungen sind implizite Konvertierungen mit eingebauter Bedeutung. Klausel 4 zählt die vollständige Menge solcher Konvertierungen auf.
Da Sie keinen Cast durchführen und keine benutzerdefinierten Conversions definiert haben, führen Sie tatsächlich eine solche Standardkonvertierung durch. Ohne das Aufzählen aller möglichen derartigen Conversions sind zwei für Ihr Beispiel von besonderem Interesse: pointer Conversions und Pointer to member Conversions. Beachten Sie, dass C ++ Zeiger auf Elementtypen nicht als Teilmenge von Zeigertypen betrachtet.
Zeiger auf Elementkonvertierungen sind in C ++ 11§4.11 definiert und bestehen aus genau zwei Konvertierungen:
"Zeiger auf Member von B vom Typ cv T", wobei B ein Klassentyp ist, kann in einen [...] "Zeiger auf Member von D vom Typ cv T" konvertiert werden, wobei D eine abgeleitete Klasse ist [...] von B
void
(4.10 / 2), was die Umwandlung eines beliebigen Zeigertyps in einen Zeiger auf void
ermöglicht. A [...] "pointer to cv D", wobei D ein Klassentyp ist, kann in einen [...] Zeiger auf cv B umgewandelt werden, wobei B eine Basisklasse [...] ist. ] von D
C ++ lässt diese Konvertierung und viele andere nicht zu, da dies die Implementierung der virtuellen Vererbung erschweren würde.
%Vor%So könnte ein Compiler versuchen, diese Klassen anzuordnen:
%Vor%Wenn wir einen Zeiger auf B haben, ist es keine leichte Aufgabe, einen Zeiger auf seine Basisklasse A zu bekommen, weil er nicht in einem festen Abstand vom Anfang des B-Objekts liegt. A kann sich direkt neben B :: b befinden oder anderswo, abhängig davon, ob unser Objekt ein eigenständiges B oder ein B ist, das eine Basis von D ist. Es gibt keine Möglichkeit zu wissen, welchen Fall wir haben!
Um eine Umwandlung zu machen, muss das Programm tatsächlich auf das B-Objekt zugreifen und einen versteckten Basiszeiger daraus erhalten. Das wirkliche Layout würde also eher so aussehen:
%Vor% wobei address-of-A
s versteckte Elemente sind, die vom Compiler hinzugefügt wurden.
Das ist alles gut und schön, während wir über regelmäßige Hinweise sprechen. Aber wenn wir einen Zeiger auf ein Element haben, haben wir kein Objekt, von dem wir den versteckten Basiszeiger holen könnten. Wenn wir also nur B X::*
haben, gibt es absolut keine Möglichkeit, sie in A X::*
zu konvertieren, ohne ein tatsächliches X-Objekt zu haben.
Obwohl es theoretisch möglich ist, Konvertierungen wie diese zuzulassen, wäre es sehr kompliziert. Zum Beispiel müsste ein Pointer-to-Member eine variable Datenmenge speichern (all die versteckten Zeiger-zu-Basis-Werte, die das ursprüngliche Objekt hat).
Theoretisch könnte C ++ solche Umwandlungen von Zeigern zu Gliedern nur auf nicht virtuelle Basisklassen zulassen (in diesem Beispiel D X::*
bis B X::*
oder C X::*
, aber nicht A X::*
). Oder zumindest sehe ich nicht, warum es ein unüberwindbares Problem für Implementierungen sein könnte. Ich nehme an, dass dies nicht getan wird, weil es dem Standard eine zusätzliche Komplexität für einen sehr geringen Nutzen hinzufügen würde. Oder vielleicht möchte der Standard nicht ungewöhnliche Implementierungen der Vererbung ausschließen. Zum Beispiel möchte eine Implementierung möglicherweise die gesamte Vererbung mit versteckten Pointer-zu-Base-Mitgliedern implementieren, als ob sie immer virtuell wäre (zu Debuggingzwecken oder zur Kompatibilität mit anderen Sprachen oder was auch immer). Oder vielleicht wird es einfach übersehen. Vielleicht in einer anderen Überarbeitung des Standards (2020?)
Tags und Links c++