2010-06-01 8 views
12

A veces, la noción de privacidad C++ 's me acaba de deflectores :-)delegar en partes privadas

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 
} 

Desde Foo::Bar es private, no puedo declarar b en main. Sin embargo, puedo llamar a los métodos desde Foo::Bar bien. ¿Por qué demonios esto está permitido? ¿Fue eso un accidente o por diseño?


Oh, espera, se pone mejor:

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

A pesar de que no se me permite nombrar el tipo Foo::Bar, funciona muy bien con auto ...


Noah escribió:

tipo Los nombres definidos dentro de una definición de clase no se pueden usar fuera de su clase sin calificación.

Sólo por diversión, aquí es cómo se puede conseguir en el tipo de fuera:

#include <type_traits> 

const Foo some_foo(); 

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

Para abreviar el ejemplo del código, básicamente se está preguntando si es legal por diseño devolver un tipo privado ('Foo :: Bar *') desde una función miembro pública ('Foo :: operator ->()')? – stakx

+0

@stakx Hmmm, supongo que sí :) +1 – fredoverflow

+0

En cuanto al ejemplo del código 'auto', pensé que era la abreviatura de' auto int'. ¿Esto realmente compila y se ejecuta correctamente? – stakx

Respuesta

6

Tratando de encontrar nada en la norma que explicarla en detalle, pero no puedo. Lo único que puedo encontrar es 9.9:

Los nombres de tipo obedecen exactamente las mismas reglas de alcance que otros nombres. En particular, los nombres de tipos definidos dentro de una definición de clase no se pueden usar fuera de su clase sin calificación.

Esencialmente, el nombre de Foo :: Bar es privado para Foo, no es la definición. Por lo tanto, puede utilizar Bars fuera de Foo, simplemente no puede hacer referencia a ellos por tipo ya que ese nombre es privado.

Las reglas de búsqueda de nombre para los miembros también parecen tener algún efecto sobre esto. No veo nada que específicamente haga referencia a "clase anidada" y, por lo tanto, no se les permitiría (si la falta de información es porque no está allí).

3

No puedo proporcionar una respuesta completa, pero tal vez sea un punto de partida. La especificación C++ 1998 incluye el siguiente ejemplo de código bajo párrafo 11.3 [class.access] (p 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 
} 

En este ejemplo, un tipo privado está "publicado" a través de un público typedef. Aunque no es lo mismo que publicar un tipo a través de una firma de función de miembro, es similar.

+2

El mismo texto se encuentra en la cláusula 11, párrafo 4 del C++ 0x FCD; Creo que el párrafo 5 explica por qué puede hacerlo: "Cabe señalar que es el acceso a los miembros y a las clases base lo que está controlado, no su visibilidad. Los nombres de los miembros siguen visibles y las conversiones implícitas a las clases base aún se consideran. , cuando los miembros y clases base son inaccesibles. La interpretación de un constructo dado se establece sin tener en cuenta el control de acceso. Si la interpretación establecida hace uso de nombres de miembros inaccesibles o clases base, el constructo está mal formado. " –

+0

* @ Niall C.:* No estoy seguro de que la distinción entre accesibilidad y visibilidad proporcione la respuesta. La visibilidad no parece ser un problema, aquí. Y el ejemplo de código anterior todavía hace accesible un tipo privado (aunque de manera limitada). – stakx

+0

Aunque 'Foo :: Bar' es inaccesible fuera de' Foo', sus miembros son visibles y accesibles a través de la 'Bar *' que devuelve la sobrecarga 'operator ->()' .. –

1

Creo que esto es por diseño. No puede crear explícitamente una instancia de Foo::Bar pero podría devolverse desde las funciones miembro y luego podría pasarla a otras funciones miembro. Esto le permite ocultar los detalles de implementación de su clase.