reinterpret_cast für fast pod-Daten (ist Layoutkompatibilität genug)

8

Ich versuche etwas über static_cast und reinterpret_cast zu erfahren.

Wenn ich richtig bin, sagt der Standard (9.2.18), dass reinterpret_cast für Pod-Daten sicher ist:

  

Ein Zeiger auf ein POD-struct-Objekt,   geeignet umgewandelt mit a    reinterpret_cast , zeigt auf seine   Anfangs Mitglied (oder wenn dieses Mitglied ein   Bit-Feld, dann zu der Einheit, in der   es residiert) und umgekehrt. [Anmerkung: Es könnte daher unbenannt sein   padding innerhalb eines POD-struct-Objekts, aber nicht am Anfang, wie es notwendig ist, um dies zu erreichen   geeignete Ausrichtung. - Ende   Notiz]

Meine Frage ist, wie genau das zu interpretieren ist. Ist beispielsweise Layout-Kompatibilität ausreichend? und wenn nicht, warum nicht?

Für mich zeigt das folgende Beispiel ein Beispiel, in dem eine strikte "nur POD ist gültig" Interpretation falsch zu sein scheint.

%Vor%

Auch was kann möglicherweise brechen, wenn complex_base::m_data geschützt ist (was bedeutet, dass complex_base nicht pod ist)? [BEARBEITEN: und wie schütze ich mich / erkenne solche Brüche]

Mir scheint, dass die Layout-Kompatibilität ausreichen sollte - aber das scheint nicht zu sein, was der Standard sagt.

BEARBEITEN: Danke für die Antworten. Sie haben mir auch geholfen, das zu finden, Ссылка

    
Tom 20.02.2011, 23:31
quelle

2 Antworten

5
  

Ich glaube, das Folgende ist gültig, weil complex_base POD

ist

Sie liegen falsch. d[0] bezieht sich nicht auf das erste Mitglied eines complex_base -Objekts. Seine Ausrichtung kann daher für ein complex_base -Objekt nicht gut genug sein, daher ist eine solche Besetzung nicht sicher (und nicht zulässig durch den von Ihnen zitierten Text).

  

Vervollständigt das Folgende eine gültige Umwandlung in einen Komplex, obwohl Komplex NICHT POD ist?

cb1 und cb2 zeigen nicht auf Unterobjekte eines Objekts vom Typ complex , daher erzeugt static_cast undefiniertes Verhalten. Siehe 5.2.9p5 von C ++ 03

  

Wenn der Lvalue vom Typ "cv1 B" tatsächlich ein Unterobjekt eines Objekts vom Typ D ist, bezieht sich der Lvalue auf das umschließende Objekt vom Typ D. Andernfalls ist das Ergebnis der Besetzung undefiniert.

Es ist nicht genug, wenn nur die Typen zusammenpassen. Der Text spricht von einem Zeiger, der auf ein POD-struct-Objekt verweist, und von einem l-Wert, der sich auf ein bestimmtes Unterobjekt bezieht. oth complex und complex_base sind Standard-Layout-Objekte. Die C ++ 0x-Spezifikation besagt, anstelle des von Ihnen zitierten Textes:

Ist die POD-Anforderung zu streng?

Dies ist eine andere Frage, nicht in Bezug auf Ihren Beispielcode. Ja, POD-Ness ist zu streng. In C ++ 0x wurde dies erkannt, und eine neue Anforderung, die lockerer ist, ist "Standard-Layout" gegeben. Ich denke, dass sowohl complex als auch complex_base Standard-Layout-Klassen sind, nach der C ++ 0x-Definition. Die C ++ 0x-Spezifikation besagt, anstelle des von Ihnen zitierten Textes:

  

Ein Zeiger auf ein Standard-Layout-struct-Objekt, das mit einem reinterpret_cast passend konvertiert wurde, zeigt auf seinen ursprünglichen Member (oder wenn dieser Member ein Bit-Feld ist, dann auf die Unit, in der er sich befindet) und umgekehrt.

Ich interpretiere das so, als würde man einen Pointer auf ein double werfen, welches tatsächlich auf ein complex Mitglied (Mitglied durch Vererbung) zeigt, das in ein complex* geworfen wird. Eine Standardlayout-Klasse ist eine Klasse, die entweder keine Basisklassen mit nicht statischen Daten enthält oder nur eine Basisklasse mit nicht statischen Daten. Somit gibt es ein einzigartiges "Initial Member".

    
Johannes Schaub - litb 20.02.2011, 23:58
quelle
1

Was passieren kann, ist, dass Nicht-POD-Klassen-Instanzen vtable-Zeiger haben können, um virtuellen Versand zu implementieren, wenn sie virtuelle Funktionen haben, einschließlich des virtuellen dtor. Der vtbl-Zeiger ist typischerweise das erste Mitglied der Nicht-POD-Klasse.

(Technisch gesehen muss virtueller Versand nicht auf diese Weise implementiert werden; praktisch ist es das. Deshalb muss der Standard so streng sein, was als POD-Typ bezeichnet wird.)

Ich bin ehrlich gesagt nicht sicher, warum nur eine ctor ("8.5.1 (1):" Ein Aggregat ist ein Array oder eine Klasse (Klausel 9) ohne von Benutzern deklarierte Konstruktoren (12.1) ") disqualifiziert etwas davon ein POD. Aber es tut.

In dem besonderen Fall, den Sie hier haben, brauchen Sie natürlich keine Neuinterpretation. Fügen Sie stattdessen einfach einen Konvertierungsoperator zur Basisklasse hinzu:

%Vor%

Da eine complex_base kein double * ist, wendet der C ++ - Compiler einen (und nur einen) benutzerdefinierten Konvertierungsoperator an, um b zu p zuzuweisen. Das heißt, dass p = b den Konvertierungsoperator aufruft und p = b.operator double*() ergibt (und beachte, dass das eigentlich eine legale Syntax ist - du kannst Konvertierungsoperatoren direkt aufrufen, nicht das, was du tun solltest), was in diesem Fall natürlich alles zurückgibt m_data.

Beachten Sie, dass dies fraglich ist, da wir jetzt direkten Zugriff auf die Interna von b haben. In der Praxis könnten wir ein const double * oder eine Kopie oder einen Smart Copy-on-Write "Zeiger" oder .... zurückgeben.

Natürlich ist m_data in diesem Fall sowieso öffentlich, also geht es uns nicht schlechter, als wenn wir nur geschrieben hätten:

%Vor%

Wir sind eigentlich etwas besser dran, weil Clients von complex_base nicht wissen müssen, wie wie in ein double konvertiert wird.

    
tpdi 20.02.2011 23:58
quelle