J'ai été assez surpris quand j'ai vu le code suivant compiler sans erreurs ni avertissements dans g ++ - 4.2:
typedef enum test { one };
Mon hypothèse était que si vous utilisiez le mot clé typedef
, il aurait besoin d'un identifiant supplémentaire, comme dans:
typedef enum test { one } test;
Comme déjà mentionné, g ++ - 4.2 l'accepte sans même un avertissement. Clang ++ 3.0 avertit "warning: typedef requiert un nom", de même que Comeau avertit "warning: la déclaration nécessite un nom typedef" et g ++ - 4.6 informe: "warning: 'typedef' a été ignoré cette déclaration ".
Je n'ai pas été capable d'identifier où cela est autorisé dans la norme, et je trouve un peu déroutant que deux des compilateurs avertissent qu'il s'agit de required, cela ne devrait pas être une erreur si le nom-typedef est requis mais non présent?
UPDATE: J'ai coché C avec les mêmes compilateurs. Clang et comeau donnent la même sortie, gcc donne un avertissement: "warning: spécificateur de classe de stockage inutile dans une déclaration vide", ce qui semble encore plus déroutant.
UPDATE: J'ai vérifié la suppression du nom de l'énum et les résultats sont les mêmes:
typedef enum { one };
De même avec une structure nommée:
typedef struct named { int x };
Mais pas avec une structure non nommée, auquel cas le code a été rejeté dans g ++ (4.2/4.6) avec "error: nom de type manquant dans la déclaration typedef", gcc (4.2/4.6) a donné l'avertissement suivant: " warning: structure/union non nommée ne définissant aucune instance ", clang ++" warning: déclaration ne déclare rien ", comeau" error: déclaration nécessite un nom typedef "
C'est une syntaxe dégénérée qui est autorisée mais n'apporte aucun avantage. La plupart des compilateurs modernes peuvent être incités à émettre un avertissement à ce sujet; par défaut, ils ne peuvent pas. Sans le nom typedef, le mot clé typedef
est superflu; dans votre exemple, cela équivaut complètement à:
enum test { one };
Un autre endroit où cela peut se produire est avec une structure:
typedef struct SomeThing { int whatever; };
Ceci est équivalent à:
struct SomeThing { int whatever; };
Notez que typedef
est officiellement (ou syntaxiquement) un "spécificateur de classe de stockage", tel que static
, extern
, auto
et register
.
Dans ISO/IEC 9899: 1999 (c'est la norme C), nous trouvons:
§6.7 Déclarations
Syntaxe
déclaration:
déclaration-specifiers init-déclarator-listopt;
déclaration-specifiers:
storage-class-specifier spécificateurs de déclarationopt
type-specifier spécificateurs de déclarationopt
type-qualifier spécificateurs de déclarationopt
spécificateur de fonction spécificateurs de déclarationopt
init-declarator-list:
init-declarator
init-déclarator-list, init-declarator
init-declarator:
déclarateur
déclarator = initializer
Et (à la demande):
§6.7.1 Spécificateurs de classe de stockage
Syntaxe
spécificateur de classe de stockage:
typedef
extern
static
auto
register
Si vous suivez cette syntaxe, il y a beaucoup de possibilités dégénérées, et ce que vous avez montré n'est qu'un exemple parmi d'autres.
Il est possible que C++ ait des règles différentes.
Dans ISO/IEC 14882: 1998 (norme C++ d'origine), nous trouvons au § 7.1.1 'Spécificateurs de classe de stockage' que C++ ne traite pas typedef
comme une classe de stockage; la liste ajoute mutable
et exclut typedef
. Ainsi, la spécification grammaticale de typedef
en C++ est nettement différente de la spécification C.
§7 Déclarations
Les déclarations spécifient comment les noms doivent être interprétés. Les déclarations ont la forme
déclaration-seq:
déclaration
declaration-seq declaration
déclaration:
déclaration de bloc
définition de fonction
template-declaration
instanciation explicite
spécialisation explicite
spécification de liaison
définition d'espace de nom
déclaration en bloc:
simple-declaration
asm-definition
namespace-alias-definition
using-declaration
directive-utilisatrice
déclaration simple:
decl-specifier-seqopt init-déclarator-listopt ;
...
¶5 Si le décl.-Specifier-seq contient le spécificateur
typedef
, la déclaration s'appelle une déclarationtypedef
et le nom de chaqueinit-declarator
est déclaré être un nom typedef, synonyme de son type associé (7.1.3).§7.1 Spécificateurs [dcl.spec]
Les spécificateurs pouvant être utilisés dans une déclaration sont
décl-spécificateur:
spécificateur de classe de stockage
spécificateur de type
spécificateur de fonction
friend
typedef
decl-specifier-seq:
decl-specifier-seqopt
decl-specifier
§7.1.1 Spécificateurs de classe de stockage [dcl.stc]
spécificateur de classe de stockage:
auto
register
static
extern
mutable
§7.1.2 Spécificateurs de fonction [dcl.fct.spec]
spécificateur de fonction:
inline
virtual
explicit
§7.1.3 Le spécificateur typedef [dcl.typedef]
Déclarations contenant le décl-specifier
typedef
déclarer des identificateurs pouvant être utilisés ultérieurement pour nommer types fondamentaux (3.9.1) ou composés (3.9.2). Le spécificateurtypedef
ne doit pas être utilisé dans une définition de fonction (8.4), et il ne doit pas être combiné dans une déclaration-spécificateur-seq avec tout autre type de spécificateur sauf un spécificateur de type.nom de typedef:
identifiant
...
Dans une portée donnée, un spécificateur typedef peut être utilisé pour redéfinir le nom de tout type déclaré dans cette portée se référer au type auquel il fait déjà référence. [Exemple:
typedef struct s { /* ... */ } s; typedef int I; typedef int I; typedef I I;
—Fin exemple]
§7.1.4 Le spécificateur d'ami [dcl.friend]
Le spécificateur de contact est utilisé pour spécifier l'accès aux membres de la classe. voir 11.4.
§7.1.5 Spécificateurs de type [dcl.type]
spécificateur de type:
spécificateur-type-simple
spécificateur de classe
enum-specifier
spécificateur-type-élaboré
cv-qualifier
Puisque le §7 ¶5 dit que les noms typedef
viennent du init-declarator et que le init-declarator-list est marqué 'opt', je pense que cela signifie que le nom typedef
peut être omis en C++, tout comme en C.
La seule chose que je pouvais trouver était la suivante dans le §7.1.3 [dcl.typedef] p1
standard C++ 03:
typedef-name:
- identifiant
Un nom déclaré avec le spécificateur
typedef
devient un typedef-name.
Remarquez le manquant opt après identifier, ce qui m'indique au moins qu'un identifiant est nécessaire pour le typedef-name. Étrange que tous les compilateurs testés (en silence) acceptent cela.
Edit : Après la réponse de @ Jonathan, j'ai trouvé ce qui suit dans le même standard que ci-dessus:
decl-specifier:
- storage-class-specifier
- type-specifier
- fonction-spécificateur
friend
typedef
Comme on peut le voir, cela fournit un cas supplémentaire pour typedef
et la liste de storage-class-specifiers le confirme:
spécificateur de classe de stockage:
auto
register
static
extern
mutable
Nous sommes donc aussi désemparés qu’avant dans le cas du C++.
Pour moi, cela ressemble vraiment à une différence entre C et C++. C++ tape implicitement des structures et des unions dans leurs balises; donc ajouter le typedef est superflu, mais pas une erreur. Je ne sais pas si cela fonctionne aussi pour les enums.
La chose à faire ensuite est de voir quelles définitions de variables sont autorisées après ces déclarations.
enum test etest;
test etest2;
struct named snamed;
named snamed2;