2010-11-28 7 views
13

Ich habe gerade die folgenden Absätze in C++ 03 Standardentwurf relevant für den Zeiger auf die Elementkonvertierung gefunden. Zeiger auf Elementkonvertierung

4,11/2 Pointer an dem Mitglied Conversions

rvalue des Typs „Zeiger auf Mitglied von B vom Typ cv T“, wobei B einen Klasse-Typ ist, können zu einem R-Wert vom Typ umgewandelt werden "Zeiger auf Member von D vom Typ cv T", wobei D eine abgeleitete Klasse (Abschnitt 10) von B ist. Wenn B eine unzugängliche (Klausel 11), mehrdeutige (10.2) oder virtuelle (10.1) Basisklasse von D ist, a Programm, das diese Umwandlung erfordert, ist schlecht ausgebildet. Das Ergebnis der Konvertierung bezieht sich auf dasselbe Element wie der Zeiger auf das Element vor der Konvertierung, bezieht sich jedoch auf das Basisklassenelement, als wäre es ein Element der abgeleiteten Klasse . Das Ergebnis bezieht sich auf das Element in Ds Instanz von B. Da das Ergebnis den Typ "Zeiger auf Member von D vom Typ CV T" hat, kann es mit einem D-Objekt dereferenziert werden. Das Ergebnis ist das gleiche, als wenn der Zeiger auf Glied von B mit dem B-Unterobjekt von D. Der Nullelement Zeigerwert zu dem Nullelement Zeigerwert des Ziel type.52 dereferenziert wurde umgewandelt wird)

5.2.9/9 static_cast

rvalue des Typs „Zeiger auf Mitglied D vom Typ CV1 T“ können zu einem R-Wert des Typs „Zeiger auf Mitglied von B vom Typ CV2 T“ umgewandelt werden, wobei B ist eine Basisklasse (Klausel 10) von D, wenn eine gültige Standardkonvertierung von "Zeiger auf Element von B vom Typ T" nach "Zeiger auf Element von D vom Typ T" existiert (4.11) und cv2 dasselbe cv ist -Qualifikation als oder höher cv-qualification als, cv1.63) Der Nullelementzeigerwert (4.11) wird in den Nullelementzeigerwert des Zieltyps konvertiert. Wenn Klasse B das ursprüngliche Element enthält oder eine Basisklasse oder eine abgeleitete Klasse der Klasse ist, die das ursprüngliche Element enthält, verweist der resultierende Zeiger auf Element auf das ursprüngliche Element. Andernfalls ist das Ergebnis der Besetzung undefiniert. [Hinweis: Obwohl Klasse B nicht das ursprüngliche Element enthalten muss, muss der dynamische Typ des Objekts, auf dem der Zeiger auf Element dereferenziert wird, das ursprüngliche Element enthalten; siehe 5.5. ]

Also hier ist meine Frage. Wie 5.2.9/9 sagt, kann ein Zeiger auf Member von D in einen Zeiger auf Member von B umgewandelt werden, wenn eine gültige Konvertierung existiert, die in 4.11/2 beschrieben ist. Bedeutet dies, dass, wenn es ein Mitglied 'm' von D gibt, das nicht von B geerbt wurde, der Zeiger auf Element 'm' nicht auf den Typ des Zeigers auf Element von B geworfen werden kann?

class Base { }; 
class Derived : public Base 
{ 
    int a; 
}; 
typedef int Base::* BaseMemPtr; 
BaseMemPtr pa = static_cast<BaseMemPtr>(&Derived::a); // invalid, as per 5.2.9/9 ? 

In der Notiz von 5.2.9/9, heißt es auch, dass, obwohl die Klasse B das ursprüngliche Element nicht enthalten muss, den dynamischen Typen des Objekts, auf das der Zeiger auf ein Element dereferenziert wird das ursprüngliche Element enthalten muss .

Ich bin verwirrt mit dem Wortlaut des Absatzes. Ist der obige Code gültig?

Ich suchte die Website, und es gibt eine ähnliche Frage, c++ inheritance and member function pointers, deren Antwort nur den Fall, dass die Konvertierung von Zeiger zu Mitglied der Basisklasse Zeiger auf Mitglied der abgeleiteten Klasse abgedeckt.

+0

Sie weisen einem Zeiger auf Elementfunktionsvariable einen Zeiger auf Datenelementwert zu. Ansonsten, yep, "das Ergebnis der Besetzung ist undefiniert." – Potatoswatter

+0

Danke, ich habe es behoben. – ashen

Antwort

10

Der Code, den Sie geschrieben haben, ist absolut gültig. Es ist nichts falsch daran (abgesehen davon, dass Derived::a privat ist). Es ist wohlgeformt und das Verhalten ist (soweit) definiert. Wie der zitierte Teil des Standards sagt, ist es vollkommen legal, Mitgliedszeiger mit einem expliziten static_cast zu aktualisieren, was genau das ist, was Sie tun. 5.2.9/9 sagt nie, dass das spitze Mitglied in der Basisklasse vorhanden sein muss.

Auch, wie Sie richtig aus dem Standard notiert, die Anwesenheit des tatsächlichen Mitglieds in dem Objekt später erforderlich zur Zeit von dereferenzieren des Zeiger, nicht zur Zeit der Initialisierung. Dies hängt natürlich vom dynamischen Typ des Objekts ab, das auf der linken Seite des Mitgliedszugriffsoperators (->* oder .*) verwendet wird. Der Typ ist nur zur Laufzeit bekannt und kann daher vom Compiler nicht überprüft werden.

Diese Anforderung als bloße Note in 5.2.9/9 enthalten, aber es ist in eine formelle Form in 5,5/4

4 Wenn der dynamische Typ des Objekts enthält nicht wiederholt das Mitglied, auf das der Zeiger verweist, ist das Verhalten undefined.

So zum Beispiel im Zusammenhang mit Ihrem Beispiel der folgenden Codezeilen sind

wohlgeformt
Base b; 
b.*pa; // 1 

Derived d; 
d.*pa; // 2 

Base *pb = &d; 
pb->*pa; // 3 

jedoch das erste dereferenzieren erzeugt nicht definiertes Verhalten (da Objekt b nicht das Element enthält), während sowohl der zweite als auch der dritte vollkommen legal sind.

+0

Ausgezeichnete Antwort. Aber ich kann nicht widerstehen zu erwähnen, dass "sizeof b. * Pa" ein definiertes Verhalten gibt, trotz "Dereferenzierung" eines nicht existierenden Mitglieds. (Weil nichts in C++ einfach ist ... :-P) –

+1

@j_random_hacker: Nun, ja. In meinem Fall ist das Auftreten von undefiniertem Verhalten mit dem Prozess der Pointer-Dereferenzierung während der * Auswertung * des Ausdrucks verbunden, während im Fall von 'Größe von' das Argument niemals ausgewertet wird. – AnT

+0

VC gibt C4407 Warnung aus, wenn ich einen Zeiger zum Funktionsmitglied der abgeleiteten Klasse auf den zweiten Basistyp static_cast, also nach dem Standard und Ihrer Antwort, ist es nicht standardkonform, oder? – ashen