web-dev-qa-db-fra.com

Que signifie la syntaxe C ++ "A :: B: A {};"

Qu'est-ce que la syntaxe C++ struct A::B:A {}; signifier? Où cette définition de nom (ou d'accès) est-elle décrite dans la norme C++?

#include <iostream>

struct B;

struct A {
    struct B;
};

struct A::B:A {
};

int main() {
    A::B::A::B b;
    std::cout<<"Sizeof A::B::A::B is " << sizeof(A::B::A::B)<<std::endl;
    return 0;
}
64
devsh

Cette définition

struct A {
    struct B;
};

Définit une struct A avec la déclaration d'une struct imbriquée B1. Le nom complet de B est A::B, vous pourriez dire que B se trouve dans le "namespace" de A. Ensuite ceci:

struct A::B : A { // Note I added spaces
};

Est-ce que la définition de A::B, et le single : spécifie qu'il est dérivé de A.

Maintenant, la partie intéressante est A::B::A::B. Disons le disséquer:

  1. A::B nomme la structure imbriquée.
  2. A::B::A accède au nom de classe injecté A à l'intérieur de B. L'injection est due à l'héritage.
  3. A::B::A::B nomme à nouveau la structure imbriquée B dans A.

Et vous pouvez continuer ad-infinitum, ou au moins jusqu'à ce que votre compilateur atteigne sa limite de traduction.2.

Un exercice intellectuel amusant, mais évitez comme la peste dans le code réel.


[class.qual]/1 explique le fonctionnement de la recherche

Si le spécificateur de nom imbriqué d'un id-qualifié désigne un class, le nom spécifié après le spécificateur de nom imbriqué est recherché dans l'étendue de la classe ([class.member.lookup]), sauf pour les cas énumérés ci-dessous. Le nom doit représenter un ou plusieurs membres de cette classe ou de l'une de ses classes de base (Clause [class.derived]).

Et le texte ci-dessus nous permet de nommer la classe de base car [class]/2

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 la classe injectée . Aux fins de la vérification d'accès, le nom de classe injecté est traité comme s'il s'agissait d'un nom de membre public.

Ce qui précède indique clairement que commencer un nom qualifié complet avec A:: vous permet de spécifier un membre ou une classe de base. Comme A n’a pas de base, vous ne pouvez spécifier que A::B _ (un "type de membre"). Mais A::B nomme également une classe. Nous pouvons donc spécifier une base ou un membre de qui aussi bien avec A::B::, qui nous permet de nommer A::B::A. Maintenant, rincez et répétez.


1 - Notez que c'est un tout autre B. Pas du tout lié au global struct B.
2 - Un minimum recommandé de 256 selon [implimits] /2.36

119
StoryTeller

Tout d'abord, struct B; Est une déclaration aval de struct B dans un espace de noms global. Cela peut prêter à confusion car cela n’est pas pertinent dans cet exemple. Ce global B est accessible sous la forme ::B Ou simplement B.

struct A {
    struct B;
};

Une définition de struct A dans un espace de noms global avec une déclaration de type imbriquée struct B (différente de celle précédemment déclarée B dans un espace de noms global ) Ce B imbriqué est accessible en tant que ::A::B Ou A::B.

struct A::B:A {
};

Définition de la structure imbriquée B de la structure A qui hérite de A (sans spécificateur d'accès omis). Il peut être réécrit pour:

struct A::B
:   public A
{
};

Notez que l'écriture de la définition de la structure imbriquée B à l'intérieur de la définition A comme celle-ci ne fonctionnera pas:

struct A {
    struct B: A { // error: A is incomplete at this point
    };
};

Et enfin A::B::A Fait référence à la classe de base de la structure imbriquée B, c'est-à-dire à A, de sorte que A::B::A::B Équivaut à seulement A::B.

21
VTT