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)?
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.
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
}
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;
}
Merci à Quentin de m'avoir rappelé l'échappatoire Harry.
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++.