2012-11-23 12 views
118

Sono stato in qualche modo sorpreso che il seguente codice compilato ed eseguito (vc2012 & gcc4.7.2)Perché posso utilizzare l'auto su un tipo privato?

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; 
} 

È vero che questo codice compila bene? E perché è corretto? Perché posso usare auto su un tipo privato, mentre non posso usare il suo nome (come previsto)?

+10

Si osservi che 'f.Baz() I' è anche OK, come è' std :: cout << typeid (f.Baz()). name() '. Il codice al di fuori della classe può "vedere" il tipo restituito da 'Baz()' se riesci a prenderlo, non puoi chiamarlo. –

+2

E se pensi che sia strano (cosa che probabilmente fai, visto che te lo chiedi) non sei l'unico;) Questa strategia è molto utile per cose come [Safe-Bool Idiom] (http: // www .artima.com/cppsource/safebool.html) però. –

+2

Penso che la cosa da ricordare sia che 'private' è lì come una comodità per descrivere le API in un modo che il compilatore può aiutare a far rispettare. Non ha lo scopo di impedire l'accesso al tipo 'Bar' da parte degli utenti di' Foo', quindi non ostacola 'Foo' in alcun modo dall'offrire quell'accesso restituendo un'istanza di' Barra'. –

risposta

100

Le regole per auto sono, per la maggior parte, identiche a quelle per la deduzione del tipo di modello. L'esempio lavori inviati per lo stesso motivo è possibile passare oggetti di tipo private per funzioni template:

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

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

E perché siamo in grado di passare gli oggetti di tipo private per funzioni template, vi chiederete? Perché solo il nome del tipo è inaccessibile. Il tipo stesso è ancora utilizzabile, motivo per cui è possibile restituirlo al codice cliente.

+25

E per vedere che la privacy del * nome * non ha nulla a che fare con il * tipo *, aggiungi 'public: typedef Bar return_type_from_Baz;' alla classe 'Foo' nella domanda. Ora il tipo può essere identificato da un nome pubblico, nonostante sia definito in una sezione privata della classe. –

+1

Per ripetere @ il punto di Steve: l'identificatore di accesso per il _name_ non ha nulla a che fare con il suo _type_, come visto aggiungendo 'private: typedef Bar return_type_from_Baz;' a 'Foo', come [dimostrato] (http://ideone.com/iKv2bQ). Gli identificatori 'typedef' sono ignari di accedere agli specificatori, pubblici e privati. – damienh

+0

Questo non ha senso per me. Il _name_ del tipo è semplicemente un alias per il tipo effettivo. Che importa se lo chiamo 'Bar' o' SomeDeducedType'? Non è che io possa usarlo per ottenere membri privati ​​di 'classe Foo' o altro. – einpoklum

98

Il controllo di accesso viene applicato ai nomi . Confronta con questo esempio dallo standard:

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

Per aggiungere agli altri (buono) risposte, ecco un esempio da C++ 98 che illustra che la questione in realtà non ha a che fare con auto affatto

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 
} 

L'utilizzo del tipo privato non è vietato, era solo la denominazione del tipo. Creare un temporaneo senza nome di quel tipo va bene, per esempio, in tutte le versioni di C++.

8

Questa domanda ha già avuto una risposta molto buona sia dal freddo che da R. Martinho Fernandes.

io proprio non poteva lasciarsi sfuggire l'occasione per rispondere a una domanda con un'analogia Harry Potter:.

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

Non è un 'amico di classe Harry; 'manca lì dentro? – Quentin

Problemi correlati