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
};

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.


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.


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

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.


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.


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.


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


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.


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.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.