web-dev-qa-db-fra.com

Pourquoi puis-je utiliser auto sur un type privé?

J'ai été en quelque sorte surpris que le code suivant se compile et s'exécute (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;
}

Est-il exact que ce code se compile bien? Et pourquoi est-ce correct? Pourquoi puis-je utiliser auto sur un type privé, alors que je ne peux pas utiliser son nom (comme prévu)?

136
hansmaad

Les règles pour auto sont, pour la plupart, les mêmes que pour la déduction de type de modèle. L'exemple publié fonctionne pour la même raison que vous pouvez transmettre des objets de types privés aux fonctions de modèle:

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

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

Et pourquoi pouvons-nous passer des objets de types privés à des fonctions de modèle, demandez-vous? Parce que seul le nom du type est inaccessible. Le type lui-même est toujours utilisable, c'est pourquoi vous pouvez le retourner au code client.

113

Le contrôle d'accès est appliqué à noms. Comparez avec cet exemple de la norme:

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
}
107
chill

Cette question a déjà été très bien répondu par Chill et R. Martinho Fernandes.

Je ne pouvais tout simplement pas laisser passer l'occasion de répondre à une question avec une analogie avec 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

Merci à Quentin de m'avoir rappelé l'échappatoire Harry.

12
jpihl

Pour ajouter aux autres (bonnes) réponses, voici un exemple de C++ 98 qui illustre que le problème n'a vraiment rien à voir avec auto

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'utilisation du type privé n'est pas interdite, il s'agissait uniquement de nommer le type. La création d'un temporaire sans nom de ce type est correcte, par exemple, dans toutes les versions de C++.

8
Chris Beck