¿Por qué puedo usar auto en un tipo privado?


139

De alguna manera me sorprendió que el siguiente código se compile y se ejecute (vc2012 y 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;
}

¿Es correcto que este código se compile bien? ¿Y por qué es correcto? ¿Por qué puedo usar autoun tipo privado, mientras que no puedo usar su nombre (como se esperaba)?


11
Observe que f.Baz().itambién está bien, tal como está std::cout << typeid(f.Baz()).name(). El código fuera de la clase puede "ver" el tipo devuelto por Baz()si puede obtenerlo, simplemente no puede nombrarlo.
Steve Jessop

2
Y si crees que es extraño (lo que probablemente haces, ya que estás preguntando al respecto) no eres el único;) Sin embargo, esta estrategia es muy útil para cosas como el idioma de Safe-Bool .
Matthieu M.

2
Creo que lo que hay que recordar es que privateexiste una conveniencia para describir las API de una manera que el compilador pueda ayudar a hacer cumplir. No está destinado a evitar el acceso al tipo Barpor parte de los usuarios Foo, por lo que no obstaculiza Foode ninguna manera ofrecer ese acceso al devolver una instancia de Bar.
Steve Jessop

1
"¿Es correcto que este código se compile bien?" No. Necesitas hacerlo #include <iostream>. ;-)
LF

Respuestas:


113

Las reglas para autoson, en su mayor parte, las mismas que para la deducción de tipo de plantilla. El ejemplo publicado funciona por la misma razón por la que puede pasar objetos de tipos privados a funciones de plantilla:

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

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

¿Y por qué podemos pasar objetos de tipos privados a funciones de plantilla? Porque solo el nombre del tipo es inaccesible. El tipo en sí sigue siendo utilizable, por lo que puede devolverlo al código del cliente.


32
Y para ver que la privacidad del nombre no tiene nada que ver con el tipo , agregue public: typedef Bar return_type_from_Baz;a la clase Fooen la pregunta. Ahora el tipo se puede identificar por un nombre público, a pesar de estar definido en una sección privada de la clase.
Steve Jessop

1
Para repetir @ punto de Steve: el especificador de acceso para el nombre no tiene nada que ver con su tipo , como se ve mediante la adición private: typedef Bar return_type_from_Baz;de Foo, como demostrado . typedefLos identificadores 'd son ajenos al acceso a los especificadores, públicos y privados.
damienh

Esto no tiene ningún sentido para mí. El nombre del tipo es simplemente un alias para el tipo real. ¿Qué importa si lo llamo Baro SomeDeducedType? No es que pueda usarlo para llegar a miembros privados de class Foonada.
einpoklum

107

El control de acceso se aplica a los nombres . Compare con este ejemplo del estándar:

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

Esta pregunta ya ha sido respondida muy bien por Chill y R. Martinho Fernandes.

Simplemente no podía dejar pasar la oportunidad de responder una pregunta con una analogía de 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

Gracias a Quentin por recordarme la escapatoria de Harry.


55
No hay un friend class Harry;desaparecido allí?
Quentin

@Quentin tienes toda la razón! Para completar, uno probablemente también debería agregar friend class Dumbledore;;)
jpihl

Harry no muestra que no tenga miedo llamando Wizard::LordVoldemort;al C ++ moderno. En cambio, él llama using Wizard::LordVoldemort;. (No se siente tan natural usar Voldemort, honestamente. ;-)
LF

8

Para añadir a las otras respuestas (bueno), he aquí un ejemplo de C ++ 98 que ilustra que el tema realmente no tiene que ver con autonada

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
}

El uso del tipo privado no está prohibido, solo estaba nombrando el tipo. Crear un temporal sin nombre de ese tipo está bien, por ejemplo, en todas las versiones de C ++.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.