Pourquoi utilisez-vous typedef lors de la déclaration d'une énumération en C ++?

183

Je n'ai pas écrit de C ++ depuis des années et maintenant j'essaye d'y revenir. Je suis ensuite tombé sur ceci et j'ai pensé à abandonner:

typedef enum TokenType
{
    blah1   = 0x00000000,
    blah2   = 0X01000000,
    blah3   = 0X02000000
} TokenType;

Qu'est-ce que c'est? Pourquoi le typedefmot - clé est-il utilisé ici? Pourquoi le nom TokenTypeapparaît-il deux fois dans cette déclaration? En quoi la sémantique est-elle différente de celle-ci:

enum TokenType
{
    blah1 = 0x00000000,
    blah2=0x01000000,
    blah3=0x02000000
};
Tim Merrifield
la source

Réponses:

156

En C, déclarer votre enum de la première manière vous permet de l'utiliser comme ceci:

TokenType my_type;

Si vous utilisez le second style, vous serez obligé de déclarer votre variable comme ceci:

enum TokenType my_type;

Comme mentionné par d'autres, cela ne fait aucune différence en C ++. Je suppose que soit la personne qui a écrit ceci est un programmeur C dans l'âme, soit vous compilez du code C en C ++. Dans tous les cas, cela n'affectera pas le comportement de votre code.

Ryan Fox
la source
12
Votre question n'est correcte que pour C, mais pas pour C ++. En C ++, les énumérations et les structures peuvent être utilisées directement comme s'il y avait un typedef.
David Rodríguez - dribeas
7
Eh bien, oui, mais cela répond à la vraie question qui a été posée qui concernait vraiment "qu'est-ce que cela signifie?"
BobbyShaftoe
Est-ce donc techniquement un typedef ou une énumération?
Miek
5
C'est les deux. Vous pouvez également dire: enum TokenType_ {...}; typedef enum TokenType_ TokenType;
Ryan Fox
La réponse est complète, mais je crois que le point est que le TokenType; après la déclaration enum, c'est ce qui déclare le nom du type. La réponse n'est donc pas complète à 100%. Déclarer «la première manière» spécifie à LA FOIS une énumération et un nouveau nom de type qui est cette énumération dans un blob de syntaxe. La réponse était donc utile, mais je pense que cela pourrait être amélioré un peu. J'ai peut-être été trop dur pour voter. Alors j'ai voté pour après tout ça. Si j'étais vraiment sûr de moi, je prendrais un but pour éditer / améliorer la réponse ... mais c'est une très bonne réponse
Ross Youngblood
97

C'est un héritage C, en C, si vous faites:

enum TokenType
{
    blah1   = 0x00000000,
    blah2   = 0X01000000,
    blah3   = 0X02000000
};

vous devrez l'utiliser en faisant quelque chose comme:

enum TokenType foo;

Mais si vous faites ceci:

typedef enum e_TokenType
{
    blah1   = 0x00000000,
    blah2   = 0X01000000,
    blah3   = 0X02000000
} TokenType;

Vous pourrez déclarer:

TokenType foo;

Mais en C ++, vous ne pouvez utiliser que l'ancienne définition et l'utiliser comme si elle était dans un typedef C.

tapis
la source
1
Ce que vous dites est vrai en C. Ce n'est pas vrai en C ++.
Jonathan Leffler
49
N'est-ce pas ce que j'ai dit dans ma dernière phrase?
mat
2
@mat J'ai voté pour votre commentaire sur la dernière phrase, mais pour être honnête, il est mal formulé et déroutant.
AR
20

Vous n'avez pas besoin de le faire. En C (et non en C ++), vous deviez utiliser enum Enumname pour faire référence à un élément de données du type énuméré. Pour le simplifier, vous étiez autorisé à le saisir en un seul type de données.

typedef enum MyEnum { 
  //...
} MyEnum;

autorisait les fonctions prenant un paramètre de l'énumération à être définies comme

void f( MyEnum x )

au lieu du plus long

void f( enum MyEnum x )

Notez que le nom du nom de type n'a pas besoin d'être égal au nom de l'énumération. La même chose se produit avec les structures.

En C ++, en revanche, ce n'est pas obligatoire, car les énumérations, les classes et les structures sont accessibles directement en tant que types par leurs noms.

// C++
enum MyEnum {
   // ...
};
void f( MyEnum x ); // Correct C++, Error in C
David Rodríguez - Dribeas
la source
En fait, je pense que c'est peut-être une meilleure réponse que celle généralement acceptée, car elle explique clairement «pourquoi» c'est différent.
Ross Youngblood
11

En C, c'est un bon style car vous pouvez changer le type en quelque chose en plus d'une énumération.

typedef enum e_TokenType
{
    blah1   = 0x00000000,
    blah2   = 0X01000000,
    blah3   = 0X02000000
} TokenType;

foo(enum e_TokenType token);  /* this can only be passed as an enum */

foo(TokenType token); /* TokenType can be defined to something else later
                         without changing this declaration */

En C ++, vous pouvez définir l'énumération afin qu'elle soit compilée en C ++ ou C.

Être averti
la source
Tu ne veux pas dire In C++ you can *typedef* the enum so that it will compile as C++ or C.? Vous avez dit: In C++ you can define the enum so that it will compile as C++ or C.Remarquez comment j'ai changé votre defineen typedef. Eh bien ... je suppose typedefqu'ing est déterminant.
Gabriel Staples
6

Holdover de C.

Tim
la source
Je ne sais pas que le qualificatif «précoce» est pertinent; vous écririez toujours cela en C si vous vouliez utiliser le nom du type sans le préfixe enum.
Jonathan Leffler
1
vrai. Je vais le supprimer. Je n'ai pas suivi la spécification C depuis longtemps. J'étais trop paresseux pour vérifier la distinction c / c ++ ... -1 pour moi.
Tim
6

Certaines personnes disent que C n'a pas d'espaces de noms, mais ce n'est pas techniquement correct. Il en a trois:

  1. Tags ( enum, unionetstruct )
  2. Étiquettes
  3. (tout le reste)

typedef enum { } XYZ;déclare une énumération anonyme et l'importe dans l'espace de noms global avec le nom XYZ.

typedef enum ABC { } XYZ;déclare une énumération nommée ABCdans l'espace de noms de balise, puis l'importe dans l'espace de noms global commeXYZ .

Certaines personnes ne veulent pas se soucier des espaces de noms séparés, alors elles tapent tout. D'autres ne tapent jamais car ils veulent l'espacement des noms.

russbishop
la source
Ce n'est pas exactement exact. Les structures, unions et énumérations sont référencées par nom de balise (sauf si elles sont anonymes, comme vous l'avez mentionné). Il existe des espaces de noms distincts pour les types et les balises. Vous pouvez avoir un type avec exactement le même nom qu'une balise et compiler correctement. Cependant, si une énumération a la même balise qu'une structure, il s'agit d'une erreur de compilation exactement comme le seraient 2 structures ou 2 énumérations avec la même balise. Vous oubliez également que les étiquettes pour goto sont un espace de noms séparé. Une étiquette peut avoir le même nom qu'une étiquette, ou un identificateur, mais pas un type, et un identificateur peut avoir le même nom qu'une étiquette, mais pas un type.
Rich Jahn
3

C'est un peu vieux, mais de toute façon, j'espère que vous apprécierez le lien que je suis sur le point de taper car je l'ai apprécié quand je l'ai rencontré plus tôt cette année.

Ici , il est . Je devrais citer l'explication qui me vient toujours à l'esprit lorsque je dois saisir des typedefs méchants:

Dans les déclarations de variables, les noms introduits sont des instances des types correspondants. [...] Cependant, lorsque le typedefmot - clé précède la déclaration, les noms introduits sont des alias des types correspondants

Comme beaucoup de gens l'ont déjà dit, il n'est pas nécessaire d'utiliser des typedefs déclarant des énumérations en C ++ . Mais c'est l'explication de la syntaxe du typedef! J'espère que ça aide (Probablement pas OP, puisque ça fait presque 10 ans, mais quiconque a du mal à comprendre ce genre de choses).

Francisco Orlandini
la source
1

Dans certains guides de style de code C, on dit que la version typedef est préférée pour "clarté" et "simplicité". Je ne suis pas d'accord, car le typedef obscurcit la nature réelle de l'objet déclaré. En fait, je n'utilise pas de typedefs car lorsque je déclare une variable C, je veux être clair sur ce qu'est réellement l'objet. Ce choix m'aide à me souvenir plus rapidement de ce que fait réellement un ancien morceau de code et aidera les autres à maintenir le code à l'avenir.

AdRiX
la source
1

La réponse réelle à la question «pourquoi» (qui est étonnamment ignorée par les réponses existantes en haut de cette ancienne question) est que cette enumdéclaration est probablement située dans un fichier d'en-tête qui est destiné à être compilable de manière croisée en tant que code C et C ++ (c.-à-d. inclus dans les fiules d'implémentation C et C ++). L'art d'écrire de tels fichiers d'en-tête repose sur la capacité de l'auteur à sélectionner des fonctionnalités linguistiques qui ont la signification compatible appropriée dans les deux langues.

Fourmi
la source