2010-06-01 14 views
12

Czasami pojęcie C++ 's prywatności tylko deflektory mnie :-)delegowania na części prywatnych

class Foo 
{ 
    struct Bar; 
    Bar* p; 

public: 

    Bar* operator->() const 
    { 
     return p; 
    } 
}; 

struct Foo::Bar 
{ 
    void baz() 
    { 
     std::cout << "inside baz\n"; 
    } 
}; 

int main() 
{ 
    Foo::Bar b; // error: 'struct Foo::Bar' is private within this context 

    Foo f; 
    f->baz();  // fine 
} 

Od Foo::Bar jest private, nie mogę zadeklarować b w main. Mogę jednak nazwać metody od Foo::Bar dobrze. Dlaczego, do diabła, jest to dozwolone? Czy to był wypadek czy projekt?


Oh wait, to staje się lepsze:

Foo f; 
auto x = f.operator->(); // :-) 
x->baz(); 

Mimo, że nie wolno mi nazwać rodzaj Foo::Bar, współpracuje tylko z auto ...


Noe napisał:

typ nazwy zdefiniowane w definicji klasy nie mogą być używane poza ich klasą bez kwalifikacji.

Tylko dla zabawy, oto jak można dostać w rodzaju z zewnątrz:

#include <type_traits> 

const Foo some_foo(); 

typedef typename std::remove_pointer<decltype(some_foo().operator->())>::type Foo_Bar; 
+8

Aby skrócić przykład kodu, zasadniczo pytasz, czy z założenia zwraca się prywatny typ ('Foo :: Bar *') z funkcji publicznej ('Foo :: operator ->()')? – stakx

+0

@stakx Hmmm, tak sądzę :) +1 – fredoverflow

+0

Odnosząc się do przykładu 'auto', pomyślałem, że był to skrót od' auto int'. Czy to faktycznie się kompiluje i działa poprawnie? – stakx

Odpowiedz

6

Próbując znaleźć coś w standardzie, które przeliterować szczegółowo, ale nie mogę. Jedyne co mogę znaleźć to: 9,9:

Nazwy typów podlegają dokładnie tym samym zasadom co inne nazwy. W szczególności nazwy typów zdefiniowane w definicji klasy nie mogą być używane poza ich klasą bez kwalifikacji.

Zasadniczo nazwa o nazwie z Foo :: Bar jest prywatna dla Foo, a nie definicji. W związku z tym można korzystać z pasków spoza Foo, po prostu nie można odnieść się do nich według typu, ponieważ ta nazwa jest prywatna.

Zasady wyszukiwania nazw dla członków również wydają się mieć pewien wpływ na to. Nie widzę niczego, co konkretnie odnosi się do "klasy zagnieżdżonej", a zatem nie byłoby im dozwolone (jeśli nie znajduję niczego w rzeczywistości, ponieważ jej tam nie ma).

3

Nie mogę podać pełnej odpowiedzi, ale być może punktem wyjścia. Specyfikacja C++ 1998 zawiera następujące przykład kodu pod pkt 11.3 [class.access] (str 175).:

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 
} 

W tym przykładzie, prywatny typ jest "opublikowane" w drodze publicznego typedef. Chociaż nie jest to to samo, co publikowanie typu poprzez podpis funkcji członka, jest podobne.

+2

Ten sam tekst jest w klauzuli 11, paragraf 4 C++ 0x FCD; Sądzę, że paragraf 5 wyjaśnia, dlaczego możesz to zrobić: "Należy zauważyć, że jest to dostęp do członków i klas bazowych, które są kontrolowane, a nie ich widoczność. Nazwy członków są nadal widoczne, a niejawne konwersje na klasy bazowe są nadal uważane za , gdy członkowie i klasy bazowe są niedostępne Interpretacja danego konstruktu jest ustalana bez względu na kontrolę dostępu.Jeśli ustalona interpretacja korzysta z niedostępnych nazw członków lub klas bazowych, konstrukt jest źle sformułowany. " –

+0

* @ Niall C.:* Nie jestem pewien, czy rozróżnienie między dostępnością a widocznością stanowi odpowiedź. Widoczność nie wydaje się tutaj problemem. Powyższy przykład kodu sprawia, że ​​prywatny typ jest jednak dostępny (w ograniczonym stopniu). – stakx

+0

Mimo że 'Foo :: Bar' jest niedostępny poza' Foo', jego członkowie są widoczni i dostępni poprzez 'Bar *' zwracany przez 'operator ->()' przeciążenie. –

1

Myślę, że jest to zgodne z projektem. Nie można jawnie utworzyć instancji o wartości Foo::Bar, ale można ją zwrócić z funkcji składowych, a następnie przekazać ją innym funkcjom składowym. Dzięki temu możesz ukryć szczegóły implementacji swojej klasy.

Powiązane problemy