2017-01-04 12 views
47

L'extrait ci-dessous compiles (demo):Le premier extrait ci-dessous se compile, mais pas le second. Pourquoi?

struct A{ int i = 10; }; 

int main() { 
    struct A{ int i = 20; }; 
    struct A; 
    struct A a; 
} 

Mais cela ne:

struct A{ int i = 10; }; 

int main() { 
// struct A{ int i = 20; }; 
    struct A; 
    struct A a; 
} 

je peux voir que la réponse est probablement donnée par ces paragraphes dans la norme:

[basic.lookup.elab]/2 et [basic.scope.pdecl]/7.

Mais je ne sais vraiment pas comment déduire les différents comportements montrés ci-dessus de ces deux paragraphes.

Notez que dans le premier exemple, le struct A est pas d'abord déclaré dans le -type élaboré spécificateurstruct A;, mais dans la définition de struct A dans main().

Dans le deuxième exemple, le struct A est également pas d'abord déclaré dans le élaboré type spécificateurstruct A;, mais dans la définition de struct A portée mondiale.

+0

Pourquoi le tag 'language-lawyer' a-t-il été supprimé? – giusti

+0

@giusti Je viens de réinsérer l'étiquette –

+1

Je voudrais comprendre pourquoi @RyanHaining l'a supprimé en premier lieu. Peut-être qu'il avait une raison. – giusti

Répondre

67

Chacun des exemples contient des déclarations de deux classes différentes, les deux avec le nom A.

Faisons la distinction entre les classes en renommant l'un d'eux B:

struct A{ int i = 10; }; 

int main() { 
    struct B{ int i = 20; }; 
    struct B; 
    struct B b; 
} 

Ce qui précède est sémantiquement identique à votre premier exemple. La classe A n'est jamais utilisée.

struct A{ int i = 10; }; 

int main() { 
    struct B; 
    struct B b; 
} 

Ceci est sémantiquement identique à votre deuxième exemple. Vous essayez de créer un objet de type incomplet, la classe déclarée en avant B.

Retour à B Changement de nom A ne change rien car alors la déclaration de A dans main ombre la déclaration de l'autre A à portée mondiale.

[basic.lookup.eLab]/2

Si le -type élaboré spécificateur n'a pas imbriqué-name-spécificateur, et [...] si le -type élaboré spécificateur apparaît dans une déclaration la forme:

class-keyattribute-specifier-seq optidentifier;

le -type élaboré spécificateur est une déclaration qui introduit le comme décrit dans [basic.scope.pdecl] classe nom.

Alors struct A; est une déclaration que introduit le nom de classe dans le cadre de la déclaration. En aucun cas, il ne peut se référer à une classe déclarée dans une portée externe.

[basic.scope.pdecl]/7

[Note: D'autres formes de élaborées de type spécificateur ne déclare pas un nouveau nom [...] - fin noter]

implicitement, cette forme de élaboré type spécificateur déclare un nouveau nom.

+0

1) [basic.scope.pdecl]/7 commence par l'instruction suivante: 'Le point de déclaration d'une classe ** first ** déclarée dans un spécificateur de type élaboré est le suivant:'. Cela signifie que les points (7.1) et (7.2) ne sont considérés que si la déclaration 'class A;' est ** déclarée en premier ** dans un spécificateur de type _elaborated_, ce qui n'est pas le cas dans les deux exemples, souligné à la fin de ma question. –

+0

2) Donc, en ce qui concerne [basic.scope.pdecl]/7, je ne pense pas que nous puissions dire que la déclaration 'struct A 'dans main(), dans mon second exemple, ne peut pas se référer à un classe déclarée dans la portée externe, comme vous l'avez affirmé ci-dessus. Merci pour votre réponse de toute façon. –

+0

J'ai expliqué pourquoi, dans votre second exemple, 'struct A;' ** est ** la première déclaration de 'A', parce que le' A' de la portée globale est une classe complètement différente avec le même nom. – Oktalist

44

Dans le deuxième exemple, la ligne struct A; est une déclaration forward d'une structure appelée A dans la portée de la fonction principale. Cette structure sera préférée à la structure globale struct A. La ligne suivante définit une variable appelée a de type struct A. Puisqu'un struct A a été déclaré dans la portée de la fonction principale, c'est là que le compilateur cherchera sa définition ici. Il n'arrive pas à en trouver un (c'est commenté). Le premier exemple compile parce qu'il y a une définition dans la même portée. L'exemple suivant compilera cependant, car il est précisé que A dans l'espace de noms global:

struct A{ int i = 10; }; 

int main() { 
// struct A{ int i = 20; }; 
    struct A; 
    struct ::A a; 
} 
+4

Je mentionnerais explicitement le fait qu'il s'agit d'une déclaration directe. – skypjack

+0

@skypjack Merci. Je vais réparer l'oubli. –

+2

@ FrançoisAndrieux: Je ne qualifierais pas cela d '"oubli". Votre version originale était techniquement correcte ('struct A;' est une déclaration, mais pas une définition). Cependant, écrire «forward statement» plutôt que «declaration» en fait une réponse plus facile à lire pour les juristes non-linguistes. –

5

Il ne compile pas parce qu'il ne peut pas trouver une définition pour A.

int main() { 
// struct A{ int i = 20; }; 
     struct A; 
     struct A a; 
} 

Le code ci-dessus est égal à votre premier exemple, comme le global A est remplacée par A. locale Dans la deuxième exemple A n'a pas de définition. C'est juste un prototype. Les prototypes sont censés être placés devant un morceau de code qui a besoin d'une définition lorsque la définition est placée APRÈS le code qui en a besoin. Si le compilateur ne trouve pas cette définition, il échouera car il ne sait pas ce que A est censé être (la définition globale est masquée par le prototype local, ce qui le fait ignorer).

+0

Downvoting sans commentaire? Quel est le problème avec cette réponse? – exilit

+0

@exilit Que voulez-vous dire? – theo2003

+0

Je veux dire, que quelqu'un a baissé votre réponse mais n'a pas laissé de commentaire expliquant pourquoi il l'a fait. – exilit