Kann "Elementzeiger auf abgeleitete Klasse" nicht in "Elementzeiger auf Basisklasse" umwandeln

8

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:

%Vor%

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.

    
Barnett 26.01.2015, 18:28
quelle

2 Antworten

3

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:

  • Die Nullelementzeigerkonvertierung, mit der Nullzeigerkonstanten in Zeiger auf Elementtypen (4.11 / 1) konvertiert werden können.
  • Die etwas künstlichere zweite Umwandlung (4.11 / 2):
      

    "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

  •   
  Vergleichen Sie dies mit dem vorherigen Abschnitt 4.10, der drei Zeigerkonvertierungen definiert:

  • Die Null-Pointer-Konvertierung (4.10 / 1), mit der Null-Zeiger-Konstanten in Zeigertypen umgewandelt werden können.
  • Umwandlung in Zeiger auf void (4.10 / 2), was die Umwandlung eines beliebigen Zeigertyps in einen Zeiger auf void ermöglicht.
  • Und schließlich, warum es mit Zeigern (aber nicht mit Zeigern zu Mitglied) funktioniert (4.10 / 3):
      

    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

  •   
  Um dies alles zusammenzufassen: Die Standardkonvertierung, die Sie versuchen, ist für Ihr Zeigerbeispiel definiert, existiert aber für den Zeiger auf die Elementvariante einfach nicht.     
gha.st 26.01.2015 22:22
quelle
3

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?)

    
n.m. 27.01.2015 18:11
quelle

Tags und Links