web-dev-qa-db-fra.com

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 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.


C Standard

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.


C++ Standard

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éclaration typedef et le nom de chaque init-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écificateur typedef 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.

41
Jonathan Leffler

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++.

3
Xeo

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;
0
luser droog