Le nom typedef est-il facultatif dans une déclaration typedef?
J'ai été assez surpris quand j'ai vu le code suivant compiler sans erreurs ou avertissements dans g++-4.2:
typedef enum test { one };
Mon hypothèse était que si vous utilisiez le mot-clé typedef
, Il faudrait 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 que "avertissement: typedef nécessite un nom", de même Comeau avertit que "avertissement: déclaration requiert une définition de type de nom de, et g++-4.6 informe: "avertissement: 'typedef' a été ignoré dans cette déclaration ".
Je n'ai pas été en mesure d'identifier où dans la norme cela est autorisé, et je trouve un peu déroutant que deux des compilateurs avertissent qu'il est required, ne devrait-il pas être une erreur si le nom typedef est required mais pas présent?
UPDATE : j'ai vérifié en C avec les mêmes compilateurs. Clang et comeau donnent la même sortie, gcc donne un avertissement: "warning: useless storage class spécificateur dans la déclaration vide ", ce qui semble encore plus déroutant.
UPDATE : j'ai vérifié en supprimant le nom de l'énumération 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 nouvelle structure, dans lequel cas, le code a été rejetée en g++ (4.2/4.6) avec "erreur: manque de type nom dans la définition de type-déclaration", gcc (4.2/4.6) a donné un avertissement: "avertissement: sans nom struct/union qui définit aucun cas", clang++ "avertissement: déclaration ne rien déclarer", comeau "erreur: déclaration requiert une définition de type de nom de"
3 réponses
C'est une syntaxe dégénérée qui est autorisée mais qui n'apporte aucun avantage. La plupart des compilateurs modernes peuvent être provoqués en émettant un avertissement à ce sujet; par défaut, ils ne peuvent pas. Sans le nom typedef, le mot-clé typedef
est superflu; dans votre exemple, il est complètement équivalent à:
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 {[4] } est officiellement (ou syntaxiquement) un 'spécificateur de classe de stockage', comme static
, extern
, auto
et register
.
Norme C
Dans ISO / IEC 9899: 1999 (c'est la norme C), on trouve:
§6.7 Déclarations
La Syntaxe
déclaration de:
declaration-specifiers init-declarator-listopt;
declaration-specifiers:
de stockage de classe-rédacteur de devis déclaration spécificateursopt
type-specifier declaration-specifiersopt
type de qualificatif déclaration spécificateursopt
function-specifier declaration-specifiersopt
init-declarator-list:
init-déclaration de
init-declarator-list , init-declarator
init-declarator:
déclaration de
déclaration de = initialiseur
Et (comme demandé):
§6.7.1 spécificateurs de classe de stockage
La 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é est juste l'un des nombreux.
Norme C++
Il est possible que C++ ait des règles différentes.
Dans ISO / IEC 14882: 1998 (la norme C++ originale), nous trouvons dans §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 définitivement différente de la spécification C.
Les déclarations§7 déclarations
Précisent comment les noms doivent être interprétés. Les déclarations ont la forme
Déclaration-seq:
declaration
déclaration-seq déclaration
Déclaration:
block-declaration
fonction-définition
template-declaration
instanciation explicite
explicit-specialization
liaison-spécification
namespace-definition
Block-declaration:
déclaration simple
asm-definition
namespace-alias-définition de
using-declaration
à l'aide de la directive-
Simple-déclaration:
decl-spécificateur-seqopt init-demande de déclaration-listeopt ;
...
¶5 si le spécificateur decl-seq contient le spécificateur
typedef
, la déclaration est appelée déclarationtypedef
et le nom de chaqueinit-declarator
est déclaré comme étant un nom typedef, synonyme de son type associé (7.1.3).§7.1 spécificateurs [dcl.spec]
Le les spécificateurs qui peuvent être utilisés dans une déclaration sont
Spécificateur Decl:
storage-class-specifier
spécificateur de type
function-specifier
friend
typedef
Decl-spécificateur-seq:
decl-specifier-seqopt
Decl-spécificateur
§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]
Fonction-spécificateur:
inline
virtual
explicit
§7.1.3 le spécificateur typedef [dcl.typedef]
Déclarations contenant le spécificateur decl
typedef
déclarer les identifiants qui peuvent être utilisés plus tard 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 un Decl-spécificateur-seq avec tout autre type de spécificateur sauf un spécificateur de type.Typedef-nom:
identifier
...
In a given scope, a typedef specifier can be used to redefine the name of any type declared in that scope to refer to the type to which it already refers. [Example:
{[3]}—end example]
§7.1.4 The friend specifier [dcl.friend]
The friend specifier is used to specify access to class members; see 11.4.
§7.1.5 Type specifiers [dcl.type]
Type-specifier:
spécificateur de type simple
class-specifier
enum spécificateur de
elaborated-type-specifier
cv-qualificateur
, Puisque §7 ¶5 dit que typedef
noms proviennent de la init-déclaration de et le init-demande de déclaration-liste est marqué 'opt', je pense que cela signifie que le typedef
nom peut être omis en C++, tout comme en C.
La seule chose que j'ai pu trouver était la suivante dans la norme C++03 §7.1.3 [dcl.typedef] p1
:
typedef-nom:
- identificateur
Un nom déclaré à l'
typedef
spécificateur devient un typedef-nom.
Notez le manque opt après identificateur, ce qui indique, au moins pour moi, qu'un identificateur est nécessaire pour la typedef-nom. Étrange que tous les les compilateurs testés (silencieusement) l'acceptent.
Edit : après la réponse de @Jonathan, j'ai trouvé ce qui suit dans la même norme que ci-dessus:
Decl-spécificateur :
- spécificateur de classe de stockage
- spécificateur de type
- spécificateur de fonction
friend
typedef
Comme on peut le voir, il fournit un cas supplémentaire pour typedef
et la liste sur spécificateurs de classe de stockage confirme ceci:
Spécificateur de classe de stockage:
auto
register
static
extern
mutable
Donc, nous sommes aussi désemparés qu'avant dans le cas C++.
Cela ressemble vraiment à une différence C vs C++, pour moi. C++ implicitement typedefs structs et unions à 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;