Comprendre la grammaire de typage confus

Considérez l’extrait de code suivant

typedef int type; int main() { type *type; // why is it allowed? type *k ;// which type? } 

Je reçois une erreur 'k' is not declared in this scope . Le compilateur parsing le type *k comme multiplication entre le type* et k . Cette grammaire n’est-elle pas très déroutante?

Pourquoi le type *type autorisé par le standard C ++? Parce que la grammaire le dit? Pourquoi?

La question est en fait de savoir exactement quand un nom de variable est défini comme identifiant, et le langage détermine qu’il se trouve juste après le point dans le code où la variable est déclarée:

 typedef int type; int main() { type t; // type refers to ::type int // type still refers to ::type type; // variable declared, this shadows ::type type + 1; // type is a variable of type int. } 

Il existe des règles similaires dans d’autres contextes, et il suffit simplement de décider quand les identificateurs sont déclarés. Il existe d’autres situations similaires, par exemple dans la liste d’initialisation d’une classe:

 struct test { int x; // declare member test( int x ) // declare parameter (shadows member) : x( // refers to member (parameter is not legal here) x ) // refers to parameter {}; }; 

Ou dans le cadre des identifiants dans la définition des fonctions membres:

 struct test { typedef int type; type f( type ); }; test::type // qualification required, the scope of the return type is // at namespace level test::f( type t ) // but the scope of arguments is the class, no qualification // required. {} 

En ce qui concerne la justification de la décision, je ne peux pas vous le dire, mais elle est cohérente et simple.

 type *type; // why is it allowed? 

C ++ 11 3.3.2 / 1 dit:

Le sharepoint déclaration d’un nom est immédiatement après son déclarant complet (clause 8) et avant son initialiseur (le cas échéant)

Ainsi, le type nom de la variable n’est pas introduit avant l’utilisation du type type name; le nom de type est la seule signification disponible de type pendant le déclarateur.

 type *k ;// which type? 

Le nom de la variable locale cache le nom du type global, qui est donc choisi ici. Ceci est décrit dans C ++ 11 3.3.10 / 1:

Un nom peut être masqué par une déclaration explicite du même nom dans une région déclarative ou une classe dérivée nestede.

Le nom complet du type, ::type , est bien entendu toujours disponible.

C’est déroutant, mais c’est le seul moyen d’accéder à la variable type . Si vous voulez utiliser le type, vous pouvez le faire:

 typedef int type; int main() { type *type; ::type *k ; return 0; } 

La plupart de ces monstruosités grammaticales proviennent de la compatibilité ascendante avec C.

La raison de la séparation des espaces de noms (pas dans le sens C ++, mais dans les espaces de noms variables / types) est assez évidente: lorsque vous ne polluez pas l’espace de noms avec des noms de types, moins de ruptures de code sur typedef.

Supposons qu’il y ait du code préexistant avec une variable nommée “employee”. Si les variables et les typedefs vivaient dans le même espace de nommage, un “typedef struct {} employé;” briserait le code existant, nécessitant un changement du nom de la variable (qui était plus un problème dans les jours précédant l’IDE). Cependant, s’ils ne partagent pas un espace de noms, il n’y a pas de problème et les gens ont un problème de moins à se soucier lorsqu’ils choisissent des noms de type dans de grandes bases de code.

Je pense que cela est probablement autorisé car il offre une flexibilité aux programmeurs lorsqu’ils choisissent un nom pour les variables qu’ils déclarent. En C #, vous pouvez déclarer une propriété de même nom que le type:

 //C# code class Manager { public Name Name {get;set;} }; 

Lorsque je code en C #, je trouve cette fonctionnalité très utile. Parce que j’ai plus d’options pour les noms à choisir. Sinon, si j’ai un type appelé Name , alors je ne serais pas capable de créer une propriété du même nom, je serais obligé de choisir un autre nom, par exemple Name_ , _Name , name , NAME etc. ne m’appelle pas.


Quant à votre code, puisque dans la scope (après déclaration du type object ), le type est déjà une variable, le type `type ne peut pas être référencé directement. Mais je pense que cela devrait bien se passer et selon la norme:

 typedef int type; int main() { type *type; // why is it allowed? ::type *k ;// which type? } 

Démo: http://ideone.com/chOov