2012-11-23 8 views
118

Ich war irgendwie überrascht, dass der folgende Code kompiliert und ausgeführt (vc2012 & gcc4.7.2)Warum kann ich auto für einen privaten Typ verwenden?

class Foo { 
    struct Bar { int i; }; 
public: 
    Bar Baz() { return Bar(); } 
}; 

int main() { 
    Foo f; 
    // Foo::Bar b = f.Baz(); // error 
    auto b = f.Baz();   // ok 
    std::cout << b.i; 
} 

Ist es richtig, dass dieser Code kompiliert fein? Und warum ist es richtig? Warum kann ich auto auf einer privaten Art verwenden, während ich seinen Namen nicht (wie erwartet) nicht verwenden kann?

+10

Beachten Sie, dass 'f.Baz() I' auch in Ordnung ist, wie' std :: cout << typeid (f.Baz()). name() '. Code außerhalb der Klasse kann den von "Baz()" zurückgegebenen Typ "sehen", wenn Sie ihn erreichen können, Sie können ihn einfach nicht benennen. –

+2

Und wenn Sie denken, dass es komisch ist (was Sie wahrscheinlich tun, wenn Sie sehen, wie Sie danach fragen), sind Sie nicht der einzige;) Diese Strategie ist sehr nützlich für Dinge wie den [Safe-Bool Idiom] (http: // www .artima.com/cppsource/safebool.html). –

+2

Ich denke, die Sache zu erinnern ist, dass 'private' ist es als Annehmlichkeit für APIs in einer Art und Weise zu beschreiben, die die Compiler erzwingen helfen können. Es ist nicht von den Benutzern von 'foo' zu verhindern, den Zugang zu der Art 'Bar' vorgesehen, so dass er nicht behindert 'foo' in keiner Weise aus, dass der Zugang bietet durch eine Instanz von' Bar' zurück. –

Antwort

100

Die Regeln für auto sind zum größten Teil die gleichen wie bei Typ Abzug Vorlage. Das Beispiel geschrieben Werke aus dem gleichen Grunde Sie Objekte von privaten Typen Template-Funktionen weitergeben können:

template <typename T> 
void fun(T t) {} 

int main() { 
    Foo f; 
    fun(f.Baz());   // ok 
} 

Und warum wir Objekte von privaten Typen Template-Funktionen übergeben können, fragen Sie? Weil nur der Name des Typs nicht zugänglich ist. Der Typ selbst ist noch verwendbar, weshalb Sie ihn überhaupt an den Client-Code zurückgeben können.

+25

Und um zu sehen, dass die Privatsphäre des * Namens * nichts mit dem * Typ * zu tun hat, fügen Sie 'public: typedef Bar return_type_from_Baz;' zur Klasse 'Foo' in der Frage hinzu. Jetzt kann der Typ durch einen öffentlichen Namen identifiziert werden, obwohl er in einem privaten Bereich der Klasse definiert ist. –

+1

Um @ Steves Punkt zu wiederholen: Der Zugriffsbezeichner für den _name_ hat nichts damit zu tun _type_, wie man sieht, indem man 'private: typedef Bar return_type_from_Baz;' zu 'Foo' hinzufügt, wie [demonstriert] (http://ideone.com/iKv2bQ). 'typedef''d-Identifikatoren beachten öffentliche und private Zugriffsspezifizierer nicht. – damienh

+0

Das ergibt für mich keinen Sinn. Der _name_ des Typs ist lediglich ein Alias ​​für den tatsächlichen Typ. Was macht es aus, wenn ich es 'Bar' oder' SomeDeducedType' nenne? Es ist nicht so, als könnte ich damit zu privaten Mitgliedern der 'Klasse Foo' oder so etwas gelangen. – einpoklum

98

Zutrittskontrolle wird auf Namen angelegt. Vergleichen dieses Beispiel von der Norm:

class A { 
    class B { }; 
public: 
    typedef B BB; 
}; 

void f() { 
    A::BB x; // OK, typedef name A::BB is public 
    A::B y; // access error, A::B is private 
} 
5

zu den anderen (gut) Antworten hinzuzufügen, hier ist ein Beispiel von C++ 98, die zeigt, dass das Problem wirklich nicht mit auto bei allen

zu tun hat
class Foo { 
    struct Bar { int i; }; 
public: 
    Bar Baz() { return Bar(); } 
    void Qaz(Bar) {} 
}; 

int main() { 
    Foo f; 
    f.Qaz(f.Baz()); // Ok 
    // Foo::Bar x = f.Baz(); 
    // f.Qaz(x); 
    // Error: error: ‘struct Foo::Bar’ is private 
} 

Die Verwendung des privaten Typs ist nicht verboten, er benannte nur den Typ. Das Erstellen eines unbenannten temporären dieser Art ist beispielsweise in allen Versionen von C++ in Ordnung.

8

Diese Frage wurde bereits von sehr gut sowohl Kälte und R. Martinho Fernandes beantwortet.

Ich konnte einfach nicht diese Gelegenheit sich für eine Frage mit einem Harry Potter Analogie zu beantworten:.

class Wizard 
{ 
private: 
    class LordVoldemort 
    { 
     void avada_kedavra() 
     { 
      // scary stuff 
     } 
    }; 
public: 
    using HeWhoMustNotBeNamed = LordVoldemort; 
}; 

int main() 
{ 
    Wizard::HeWhoMustNotBeNamed tom; // OK 
    Wizard::LordVoldemort not_allowed; // Not OK 
    return 0; 
} 
+4

Ist nicht eine 'Freundklasse Harry'' fehlt da drin? – Quentin

Verwandte Themen