Perché posso usare auto su un tipo privato?


139

Sono stato in qualche modo sorpreso che il seguente codice venga compilato ed eseguito (vc2012 & gcc4.7.2)

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

È corretto che questo codice venga compilato correttamente? E perché è corretto? Perché posso usare autoun tipo privato, mentre non posso usare il suo nome (come previsto)?


11
Osserva anche che f.Baz().iva bene così com'è std::cout << typeid(f.Baz()).name(). Il codice esterno alla classe può "vedere" il tipo restituito Baz()se puoi ottenerlo, semplicemente non puoi nominarlo.
Steve Jessop,

2
E se pensi che sia strano (cosa che probabilmente fai, visto che te lo stai chiedendo) non sei l'unico;) Questa strategia è tuttavia utilissima per cose come l' idioma di sicurezza .
Matthieu M.,

2
Penso che la cosa da ricordare è che privatec'è 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 Barda parte degli utenti Foo, quindi non impedisce Fooin alcun modo di offrire tale accesso restituendo un'istanza di Bar.
Steve Jessop,

1
"È corretto che questo codice venga compilato correttamente?" No. È necessario #include <iostream>. ;-)
LF

Risposte:


113

Le regole per autosono, per la maggior parte, le stesse della detrazione del tipo di modello. L'esempio pubblicato funziona per lo stesso motivo per cui è possibile passare oggetti di tipo privato a funzioni modello:

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

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

E perché possiamo passare oggetti di tipo privato alle funzioni del modello, ci chiedi? Perché solo il nome del tipo è inaccessibile. Il tipo stesso è ancora utilizzabile, motivo per cui è possibile restituirlo al codice client.


32
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 Foonella domanda. Ora il tipo può essere identificato da un nome pubblico, nonostante sia definito in una sezione privata della classe.
Steve Jessop,

1
Per ripetere il punto di @ Steve: l'identificatore di accesso per il nome non ha nulla a che fare con il suo tipo , come visto aggiungendo private: typedef Bar return_type_from_Baz;a Foo, come dimostrato . typedefgli identificatori sono ignari dell'accesso agli specificatori, pubblici e privati.
Damienh,

Questo non ha senso per me. Il nome del tipo è semplicemente un alias per il tipo effettivo. Cosa importa se lo chiamo Baro SomeDeducedType? Non è che posso usarlo per raggiungere membri privati class Fooo altro.
einpoklum,

107

Il controllo dell'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
}

12

A questa domanda ha già risposto molto bene sia il freddo che R. Martinho Fernandes.

Non potevo lasciarmi sfuggire l'opportunità di rispondere a una domanda con un'analogia di Harry Potter:

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

    friend class Harry;
};

class Harry : Wizard
{
public:
    Wizard::LordVoldemort;
};

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

https://ideone.com/I5q7gw

Grazie a Quentin per avermi ricordato la scappatoia di Harry.


5
Non friend class Harry;manca un dentro?
Quentin,

@Quentin hai assolutamente ragione! Per completezza si dovrebbe probabilmente aggiungere anche friend class Dumbledore;;)
jpihl

Harry non mostra di non aver paura chiamando il Wizard::LordVoldemort;moderno C ++. Invece, chiama using Wizard::LordVoldemort;. (Non è così naturale usare Voldemort, onestamente. ;-)
LF

8

Per aggiungere alle altre (buono) risposte, ecco un esempio da C ++ 98 che illustra che la questione in realtà non ha a che fare con autoaffatto

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'uso del tipo privato non è proibito, stava solo nominando il tipo. La creazione di un temporaneo senza nome di quel tipo va bene, ad esempio, in tutte le versioni di C ++.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.