Pourquoi y a-t-il un nom de classe injecté?

147

Récemment, j'ai vu une étrange fonctionnalité C ++: un nom de classe injecté .

class X { };
X x1;
class X::X x2; // class X::X is equal to X
class X::X::X x3; // ...and so on...

Mais je ne peux pas comprendre pourquoi cette fonctionnalité est nécessaire. Existe-t-il une pratique qui nécessite cette fonctionnalité?

Et j'ai entendu dire que cette fonctionnalité n'existait pas dans l'ancien C ++. Puis, quand a-t-il été introduit? C ++ 03? C ++ 11?

ikh
la source
Buddy, pouvez-vous regarder sur Skype? Je ne peux pas vous joindre
Irinel Iovan

Réponses:

162

Le nom de classe injecté signifie qu'il Xest déclaré comme membre de X, de sorte que la recherche de nom à l'intérieur Xtrouve toujours la classe actuelle, pas une autre Xqui pourrait être déclarée dans la même portée englobante, par exemple

void X() { }
class X {
public:
  static X create() { return X(); }
};

La create()fonction crée-t-elle un Xobjet temporaire ou appelle-t-elle la fonction X? À la portée de l'espace de noms, il appellerait la fonction, donc le but du nom de classe injecté est de s'assurer que dans le corps du Xnom trouve toujours la classe elle-même (car la recherche de nom commence dans la propre portée de la classe avant de regarder dans le portée).

C'est aussi utile dans les modèles de classe, où le nom de classe injecté peut être utilisé sans liste d'arguments de modèle, par exemple en utilisant simplement Fooau lieu de l'ID de modèle complet Foo<blah, blah, blah>, il est donc facile de se référer à l'instanciation actuelle. Voir DR 176 pour un changement entre C ++ 98 et C ++ 03 qui clarifie cela.

L'idée du nom de classe injecté était présente dans C ++ 98, mais la terminologie était nouvelle pour C ++ 03.

C ++ 98 dit:

Un nom de classe est inséré dans la portée dans laquelle il est déclaré immédiatement après que le nom de classe est vu. Le nom de la classe est également inséré dans la portée de la classe elle-même.

La deuxième phrase a été modifiée par DR 147 donc C ++ 03 dit dans [class] / 2:

Un nom de classe est inséré dans la portée dans laquelle il est déclaré immédiatement après que le nom de classe est vu. Le nom de classe est également inséré dans la portée de la classe elle-même; c'est ce qu'on appelle le nom de classe injecté .

Même avant C ++ 98, l'ARM a un libellé à peu près équivalent qui signifie que le nom de la classe peut toujours être utilisé dans le corps de la classe pour faire référence à la classe elle-même:

Le nom d'une classe peut être utilisé comme nom de classe même dans la liste des membres du spécificateur de classe lui-même.

  • Par exemple,

    class link { link* next; };

Jonathan Wakely
la source
2
On m'a posé cette question assez souvent, mais je n'ai jamais pu construire un exemple simple indiquant le problème. Donc +1 pour l'exemple.
dhein
1
Cela peut être vu clairement si vous exécutez clang ++ your_program.cpp -Xclang -ast-dump et que vous voyez votre classe puis un nœud enfant de classe injecté.
xaxxon