2012-11-23 13 views
118

Byłem zaskoczony, że w jakiś sposób następujący kod kompiluje i działa (vc2012 & gcc4.7.2)Dlaczego mogę używać auto na prywatnym?

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

Czy to prawda, że ​​ten kod kompiluje grzywny? I dlaczego jest poprawny? Dlaczego mogę używać auto na prywatnym typie, podczas gdy ja nie mogę używać jego nazwy (zgodnie z oczekiwaniami)?

+10

zauważyć, że 'f.Baz() I' jest OK, jak to' std :: cout << typid (f.Baz()). name() '. Kod spoza klasy może "zobaczyć" typ zwracany przez 'Baz()' jeśli możesz go zdobyć, po prostu nie możesz go nazwać. –

+2

A jeśli uważasz, że to dziwne (co prawdopodobnie robisz, widząc, że o to pytasz) nie jesteś jedyny;) Ta strategia jest bardzo przydatna w takich rzeczach jak [idiom Safe-Bool] (http: // www. .artima.com/cppsource/safebool.html). –

+2

Sądzę, że należy pamiętać, że "prywatny" służy jako udogodnienie w opisywaniu interfejsów API w taki sposób, w jaki kompilator może pomóc w egzekwowaniu. Nie ma to na celu uniemożliwienia dostępu do typu 'Bar' przez użytkowników' Foo', więc nie przeszkadza to 'Foo' w oferowaniu tego dostępu przez zwracanie instancji' Bar'. –

Odpowiedz

100

Reguły dla auto są w większości takie same, jak w przypadku odjęcia typu szablonu. Przykładowe prace wysłane z tego samego powodu można przekazać obiekty do prywatnych typów funkcji szablonu:

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

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

I czemu możemy przekazać obiektów prywatnych typów do funkcji szablonu, pytasz? Ponieważ tylko nazwa typu jest niedostępna. Sam typ jest nadal użyteczny, dlatego można go w ogóle zwrócić do kodu klienta.

+25

I aby zobaczyć, że prywatność * nazwy * nie ma nic wspólnego z * typem *, dodaj 'public: typedef Bar return_type_from_Baz;' do klasy 'Foo' w pytaniu. Teraz typ może być identyfikowany przez nazwę publiczną, mimo że jest zdefiniowany w prywatnej sekcji klasy. –

+1

Powtórzmy punkt @ Steve'a: specyfikator dostępu dla _name_ nie ma nic wspólnego z jego typem _type_, co widać po dodaniu 'private: typedef Bar return_type_from_Baz;' do 'Foo', jak [pokazano] (http://ideone.com/iKv2bQ). Identyfikatory 'typedef' są niepomne dla specyfikatorów dostępu, publicznych i prywatnych. – damienh

+0

To nie ma dla mnie żadnego sensu. _nazwa_ typu jest po prostu aliasem dla rzeczywistego typu. Jakie to ma znaczenie, jeśli nazywam je 'Bar' lub' SomeDeducedType'? To nie tak, że mogę go użyć, aby dostać się do prywatnych członków 'klasy Foo' lub czegoś podobnego. – einpoklum

98

Kontrola dostępu jest stosowana do nazw. Porównaj z tego przykładu z 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

Aby dodać do innych (dobrze) odpowiedzi, oto przykład z C++ 98, która pokazuje, że problem naprawdę nie mają do czynienia z auto wcale

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 
} 

Używanie prywatnego typu nie jest zabronione, było tylko nazywanie tego typu. Tworzenie nienazwanego tymczasowego tego typu jest w porządku, na przykład we wszystkich wersjach C++.

8

Na to pytanie odpowiedział już bardzo dobrze zarówno Chill, jak i R. Martinho Fernandes.

nie mogłem przepuścić tej okazji do odpowiedzi na pytanie o Harrym Potterze analogii.

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

Czy nie jest to "klasa przyjaciół Harry'ego"? – Quentin

Powiązane problemy