Por que você usa o typedef ao declarar uma enumeração em C ++?

183

Não escrevo C ++ há anos e agora estou tentando voltar a usá-lo. Eu então deparei com isso e pensei em desistir:

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

O que é isso? Por que a typedefpalavra - chave é usada aqui? Por que o nome TokenTypeaparece duas vezes nesta declaração? Como a semântica é diferente disso:

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

Respostas:

156

Em C, declarar seu enum da primeira maneira permite que você o use da seguinte maneira:

TokenType my_type;

Se você usar o segundo estilo, será forçado a declarar sua variável assim:

enum TokenType my_type;

Como mencionado por outros, isso não faz diferença no C ++. Meu palpite é que a pessoa que escreveu isso é um programador C no coração ou você está compilando o código C como C ++. De qualquer forma, isso não afetará o comportamento do seu código.

Ryan Fox
fonte
12
Sua pergunta está correta apenas para C, mas não para C ++. Em C ++, enumerações e estruturas podem ser usadas diretamente como se houvesse um typedef.
David Rodríguez - dribeas
7
Bem, sim, mas isso responde à verdadeira pergunta que foi feita sobre "o que isso significa?"
BobbyShaftoe
Então isso é tecnicamente um typedef ou um enum?
Miek
5
São os dois. Você também pode dizer: enum TokenType_ {...}; enumeração typedef TokenType_ TokenType;
Ryan Fox
A resposta está completa, mas acredito que o ponto é que o TokenType; após a declaração enum é o que está declarando o nome do tipo. Portanto, a resposta não está 100% completa. Declarar 'a primeira maneira' especifica AMBOS uma enumeração e um novo nome de tipo que é essa enumeração em um blob de sintaxe. Portanto, a resposta foi útil, mas acho que poderia melhorar um pouco. Talvez eu tenha sido duro demais para votar. Então eu votei depois de tudo isso. Se eu estava realmente certo de mim mesmo, eu tomaria uma presa na edição / melhorar a resposta ... mas esta é uma boa resp muito
Ross Youngblood
97

É uma herança C, em C, se você fizer:

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

você terá que usá-lo fazendo algo como:

enum TokenType foo;

Mas se você fizer isso:

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

Você poderá declarar:

TokenType foo;

Mas em C ++, você pode usar apenas a definição anterior e usá-la como se estivesse em um typedef C.

esteira
fonte
1
O que você diz é verdade em C. Não é verdade em C ++.
Jonathan Leffler
49
Não é o que eu disse na minha última frase?
mat
2
@mat Eu votei no seu comentário sobre a última frase, mas para ser justo, é pouco formulado e confuso.
AR
20

Você não precisa fazer isso. Em C (não C ++), era necessário o uso de enumeração Enumname para se referir a um elemento de dados do tipo enumerado. Para simplificá-lo, você pode digitá- lo em um único tipo de dados de nome.

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

funções permitidas que tomam um parâmetro da enum a serem definidas como

void f( MyEnum x )

em vez de mais

void f( enum MyEnum x )

Observe que o nome do nome do tipo não precisa ser igual ao nome da enumeração. O mesmo acontece com estruturas.

Em C ++, por outro lado, não é necessário, pois enumerações, classes e estruturas podem ser acessadas diretamente como tipos por seus nomes.

// C++
enum MyEnum {
   // ...
};
void f( MyEnum x ); // Correct C++, Error in C
David Rodríguez - dribeas
fonte
Na verdade, acho que essa pode ser uma resposta melhor do que a geralmente aceita, pois explica claramente "por que" é diferente.
Ross Youngblood
11

Em C, é bom estilo porque você pode alterar o tipo para algo além de uma enumeração.

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 */

No C ++, você pode definir o enum para que ele seja compilado como C ++ ou C.

Esteja avisado
fonte
Você não quer dizer In C++ you can *typedef* the enum so that it will compile as C++ or C.? Você disse: In C++ you can define the enum so that it will compile as C++ or C.Observe como eu mudei o seu definepara typedef. Bem ... Suponho typedefing está definindo.
Gabriel Staples
6

Ressaca de C.

Tim
fonte
Não sei se o qualificador "inicial" é relevante; você ainda escreveria isso em C se quisesse usar o nome do tipo sem o prefixo enum.
Jonathan Leffler
1
verdade. Eu vou deletá-lo. Não acompanho a especificação C há muito tempo. Eu estava com preguiça de verificar a distinção c / c ++ ... -1 para mim.
Tim
6

Algumas pessoas dizem que C não tem namespaces, mas isso não é tecnicamente correto. Tem três:

  1. Etiquetas ( enum, unione struct)
  2. Etiquetas
  3. (todo o resto)

typedef enum { } XYZ;declara uma enumeração anônima e importa-os para o namespace global com o nome XYZ.

typedef enum ABC { } XYZ;declara uma enum nomeada ABCno espaço de nomes da tag e depois a importa no espaço de nomes global como XYZ.

Algumas pessoas não querem se preocupar com os espaços para nome separados, digitando tudo. Outros nunca digitaram porque querem o espaço para nome.

russbishop
fonte
Isso não é exatamente preciso. estruturas, uniões e enumerações são referenciadas pelo nome da tag (a menos que seja anônimo, conforme mencionado). Existem namespaces separados para tipos e tags. Você pode ter um tipo com o mesmo nome exato de uma tag e compilar com precisão. No entanto, se um enum tiver a mesma tag que uma struct, esse é um erro de compilação exatamente como seriam 2 structs ou 2 enums com a mesma tag. Você também esquece que os rótulos para ir para são um espaço para nome separado. Um rótulo pode ter o mesmo nome que um rótulo ou identificador, mas não um tipo, e um identificador pode ter o mesmo nome que um rótulo, mas não um tipo.
Rich Jahn
3

Isso é meio velho, mas de qualquer maneira, espero que você aprecie o link que estou prestes a digitar, como apreciei quando o encontrei no início deste ano.

Aqui está . Devo citar a explicação que sempre está em minha mente quando preciso entender alguns typedefs desagradáveis:

Nas declarações de variáveis, os nomes introduzidos são instâncias dos tipos correspondentes. [...] No entanto, quando a typedefpalavra - chave precede a declaração, os nomes introduzidos são alias dos tipos correspondentes

Como muitas pessoas disseram anteriormente, não há necessidade de usar typedefs declarando enumerações em C ++ . Mas essa é a explicação da sintaxe do typedef! Espero que ajude (provavelmente não OP, pois já faz quase 10 anos, mas qualquer um que esteja lutando para entender esse tipo de coisa).

Francisco Orlandini
fonte
1

Em algumas guias de estilo de código C, diz-se que a versão typedef é preferida por "clareza" e "simplicidade". Eu discordo, porque o typedef ofusca a natureza real do objeto declarado. Na verdade, não uso typedefs porque, ao declarar uma variável C, quero esclarecer qual é realmente o objeto. Essa opção ajuda-me a lembrar mais rapidamente o que um pedaço de código antigo realmente faz e ajudará outras pessoas a manter o código no futuro.

AdRiX
fonte
1

A resposta real à pergunta "por que" (que é surpreendentemente ignorada pelas respostas existentes sobre essa pergunta antiga) é que essa enumdeclaração provavelmente está localizada em um arquivo de cabeçalho que se destina a ser compilável em conjunto como código C e C ++ (ou seja, incluídos nos arquivos de implementação C e C ++). A arte de escrever esses arquivos de cabeçalho depende da capacidade do autor de selecionar recursos de idioma que tenham o significado compatível adequado nos dois idiomas.

Formiga
fonte