2016-01-21 9 views
7

Ich versuche, die Konsistenz in den Fehler zu verstehen, die in diesem Programm geworfen wird:Pointer-to-Mitglied Verwirrung

#include <iostream> 

class A{ 
public: 
    void test(); 
    int x = 10; 

}; 

void A::test(){ 

    std::cout << x << std::endl; //(1) 
    std::cout << A::x << std::endl; //(2) 

    int* p = &x; 
    //int* q = &A::x; //error: cannot convert 'int A::*' to 'int*' in initialization| //(3) 


} 

int main(){ 

    const int A::* a = &A::x; //(4) 

    A b; 

    b.test(); 

} 

Der Ausgang ist 10 10. I markierte 4 Punkte des Programms, aber (3) ist meine größte Sorge:

  1. x wird normalerweise aus dem Innern einer Elementfunktion geholt.
  2. x des Objekts wird mit dem Bereichsoperator abgerufen und ein Lvalue zum Objekt x wird zurückgegeben.
  3. A::x Da lieferte eine int lvalue in (2), warum dann nicht &A::x Rückkehr nicht int* sondern int A::* zurückkehrt? Der Bereichsoperator hat sogar Vorrang vor dem Operator &, so dass A::x zuerst ausgeführt werden sollte, bevor der Wert int Lvalue zurückgegeben wird, bevor die Adresse übernommen wird. d. h. das sollte genauso sein wie &(A::x) sicher? (Das Hinzufügen von Klammern funktioniert übrigens übrigens).
  4. Ein wenig anders hier natürlich, der Geltungsbereich Operator bezieht sich auf ein Klassenmitglied, aber ohne Objekt, auf das verwiesen wird.

Warum genau funktioniert A::x nicht zurück, die Adresse des Objekts x sondern gibt die Adresse des Mitglieds, Vorrang von :: vor & ignorieren?

Antwort

3

Der C++ - Standard legt die Vorrangstellung der Operatoren nicht explizit fest; Es ist möglich, implizit aus den Grammatikregeln auf die Operatorpriorität zu schließen, aber dieser Ansatz erkennt den gelegentlichen Spezialfall wie diesen nicht, der nicht in ein traditionelles Modell der Operatorpräzedenz passt.

[expr.unary.op]/3:

Das Ergebnis des unären & Operator ist ein Zeiger auf seine Operanden. Der Operand soll ein Lvalue oder eine qualifizierte ID sein. Wenn der Operand eine qualifizierte ID ist ein nicht-statisches oder Variante Mitglieds Benennung m einer Klasse C mit Typ T, hat das Ergebnis ‚C vom Typ T Zeiger auf ein Element der Klasse‘ eingeben und ein prvalue Bezeichnen C::m . Andernfalls, wenn der Typ des Ausdrucks T ist, hat das Ergebnis den Typ 'Zeiger auf T' und ist ein Prvalue, der die Adresse des designierten Objekts oder ein Zeiger auf die designierte Funktion ist.

/4:

Ein Zeiger auf das Element wird nur gebildet, wenn eine explizite & verwendet wird und dessen Operand ein qualifizierter ID nicht in Klammern eingeschlossen. [] Hinweis: das heißt, der Ausdruck &(qualified-id), wo die qualifizierte-ID in Klammern eingeschlossen ist, bildet keinen Ausdruck des Typs "Zeiger auf Mitglied".

[expr.prim.general]/9:

A nested-name-Spezifizierer, die eine Klasse bezeichnet, gegebenenfalls gefolgt von dem Schlüsselwort template und dann durch den Namen einer gefolgt Mitglied dieser Klasse oder einer seiner Basisklassen ist eine qualifizierte ID.

Was das alles summiert sich zu ist, dass ein Ausdruck der Form &A::x den Typ „Zeiger auf Member x der Klasse A“ hat, wenn x ist ein nicht-statisches Element einer nicht gewerkschaftlich Klasse A und Betreiber die Präzedenz hat keinen Einfluss darauf.

8

Im Grunde ist es nur so, dass die Syntax &A::x (ohne Variation) so gewählt wurde, dass sie "pointer-to-member" bedeutet.

Wenn Sie beispielsweise &(A::x) schreiben, erhalten Sie den einfachen Zeiger, den Sie erwarten.

Weitere Informationen zu Zeigern zu Mitgliedern, einschließlich einer Notiz über diese Eigenschaft, finden Sie unter here.

+0

Ich denke, wenn es anders klappt, würde es zu mehr Verwirrung führen. Die gleiche Folge von Bezeichnern und Operatoren, die genau die gleichen Typen und Mitglieder referenzieren, also zwei verschiedene Dinge, je nach Umfang: Klingt nach Ärger für mich. –

+0

@ John Sensebe definitiv. Ich habe ursprünglich "Quirk" geschrieben, aber das ist in der Tat eine sehr vernünftige Wahl, obwohl es auf den ersten Blick seltsam erscheinen kann. – Quentin