Dlaczego używasz typedef, kiedy deklarujesz wyliczenie w C ++?

183

Nie pisałem żadnego C ++ od lat i teraz staram się do niego wrócić. Później natknąłem się na to i pomyślałem o poddaniu się:

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

Co to jest? Dlaczego typedefużyto tutaj słowa kluczowego? Dlaczego nazwa TokenTypepojawia się dwukrotnie w tej deklaracji? Czym różni się semantyka od tego:

enum TokenType
{
    blah1 = 0x00000000,
    blah2=0x01000000,
    blah3=0x02000000
};
Tim Merrifield
źródło

Odpowiedzi:

156

W C, zadeklarowanie wyliczenia w pierwszy sposób pozwala na użycie go w następujący sposób:

TokenType my_type;

Jeśli użyjesz drugiego stylu, będziesz zmuszony zadeklarować swoją zmienną w następujący sposób:

enum TokenType my_type;

Jak wspomnieli inni, nie robi to różnicy w C ++. Domyślam się, że albo osoba, która to napisała, jest programistą C na sercu, albo kompilujesz kod C jako C ++. Tak czy inaczej, nie wpłynie to na zachowanie twojego kodu.

Ryan Fox
źródło
12
Twoje pytanie jest poprawne tylko dla C, ale nie dla C ++. W C ++ wyliczenia i struktury mogą być używane bezpośrednio, tak jakby istniał typedef.
David Rodríguez - dribeas
7
Cóż, tak, ale to odpowiada na prawdziwe pytanie, które tak naprawdę dotyczyło „co to w ogóle oznacza?”.
BobbyShaftoe
Czy to jest technicznie typedef czy enum?
Miek
5
To jedno i drugie. Można również powiedzieć: enum TokenType_ {...}; typedef enum TokenType_ TokenType;
Ryan Fox
Odpowiedź jest kompletna, ale wierzę, że chodzi o to, że TokenType; po deklaracji wyliczenia jest to, co deklaruje nazwę typu. Tak więc odpowiedź nie jest w 100% kompletna. Deklaracja „pierwszego sposobu” określa ZARÓWNO wyliczenie i nową nazwę typu, która jest wyliczeniem w jednym obiekcie blob składni. Odpowiedź była więc pomocna, ale myślę, że można ją trochę ulepszyć. Może byłem zbyt surowy, by głosować. Po tym wszystkim głosowałem za tym. Gdybym był naprawdę pewien siebie, wziąłbym łup w edytowaniu / poprawianiu odpowiedzi ... ale to całkiem niezła odpowiedź
Ross Youngblood
97

Jest to dziedzictwo C w C, jeśli:

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

będziesz musiał go użyć, robiąc coś takiego:

enum TokenType foo;

Ale jeśli to zrobisz:

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

Będziesz mógł zadeklarować:

TokenType foo;

Ale w C ++ możesz używać tylko poprzedniej definicji i używać jej tak, jakby była w typedefinie C.

mata
źródło
1
To, co mówisz, jest prawdą w C. Nie jest prawdą w C ++.
Jonathan Leffler,
49
Czy to nie to, co powiedziałem w ostatnim zdaniu?
mat
2
@mat Głosowałem za komentarzem do ostatniego zdania, ale szczerze mówiąc, jest on źle sformułowany i mylący.
AR
20

Nie musisz tego robić. W C (nie w C ++) wymagane było użycie enumame Enumame w celu odniesienia do elementu danych wyliczonego typu. Aby uprościć to ty pozwolono typedef go do jednego typu nazwa danych.

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

dozwolone funkcje przyjmujące parametr wyliczenia jako

void f( MyEnum x )

zamiast dłuższego

void f( enum MyEnum x )

Zauważ, że nazwa typu nie musi być równa nazwie wyliczenia. To samo dzieje się z strukturami.

Z drugiej strony w C ++ nie jest to wymagane, ponieważ do wyliczeń, klas i struktur można uzyskać bezpośredni dostęp do typów po ich nazwach.

// C++
enum MyEnum {
   // ...
};
void f( MyEnum x ); // Correct C++, Error in C
David Rodríguez - dribeas
źródło
Właściwie myślę, że może to być lepsza odpowiedź niż ogólnie akceptowana, ponieważ wyjaśnia jasno „dlaczego” jest inna.
Ross Youngblood
11

W C jest to dobry styl, ponieważ możesz zmienić typ na coś oprócz wyliczenia.

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

W C ++ możesz zdefiniować wyliczenie, aby skompilowało się jako C ++ lub C.

Być ostrzeżonym
źródło
Nie chcesz powiedzieć In C++ you can *typedef* the enum so that it will compile as C++ or C.? Powiedziałeś: In C++ you can define the enum so that it will compile as C++ or C.zauważ, jak zmieniłem twój definena typedef. Cóż ... Przypuszczam typedefing jest zdefiniowanie.
Gabriel Staples
6

Holdover z C.

Tim
źródło
Nie wiem, czy „wczesny” kwalifikator jest istotny; nadal piszesz to w C, jeśli chcesz użyć nazwy typu bez przedrostka wyliczeniowego.
Jonathan Leffler,
1
prawdziwe. Usunę to. Dawno nie śledziłem specyfikacji C. Byłem zbyt leniwy, by sprawdzić dla mnie rozróżnienie c / c ++ ... -1.
Tim
6

Niektórzy twierdzą, że C nie ma przestrzeni nazw, ale nie jest to technicznie poprawne. Ma trzy:

  1. Tagi ( enum, union, i struct)
  2. Etykiety
  3. (wszystko inne)

typedef enum { } XYZ;deklaruje anonimowe wyliczenie i importuje je do globalnej przestrzeni nazw o nazwie XYZ.

typedef enum ABC { } XYZ;deklaruje wyliczenie nazwane ABCw przestrzeni nazw znaczników, a następnie importuje je do globalnej przestrzeni nazw jako XYZ.

Niektóre osoby nie chcą zawracać sobie głowy oddzielnymi przestrzeniami nazw, więc wpisują wszystko. Inni nigdy nie pisali na maszynie, ponieważ chcą przestrzeni nazw.

Russbishop
źródło
To nie jest dokładnie dokładne. struktury, związki i wyliczenia są przywoływane przez nazwę znacznika (chyba że anonimowy, jak wspomniałeś). Istnieją osobne przestrzenie nazw dla typów i tagów. Możesz mieć typ o dokładnie takiej samej nazwie jak tag i dobrze skompilować. Jednakże, jeśli wyliczenie ma ten sam znacznik co struct, jest to błąd kompilacji dokładnie tak, jak byłyby 2 struktury lub 2 wyliczenia z tym samym znacznikiem. Zapominacie także, że etykiety dla goto to osobna przestrzeń nazw. Etykieta może mieć taką samą nazwę jak tag lub identyfikator, ale nie typ, a identyfikator może mieć taką samą nazwę jak tag, ale nie typ.
Rich Jahn
3

To trochę stare, ale mam nadzieję, że docenisz link, który zamierzam wpisać, doceniając go, gdy natknąłem się na niego w tym roku.

Oto jest . Powinienem zacytować wyjaśnienie, które zawsze przychodzi mi do głowy, gdy muszę zrozumieć kilka nieprzyjemnych maszynopisów:

W deklaracjach zmiennych wprowadzane nazwy są instancjami odpowiednich typów. [...] Jednak gdy typedefsłowo kluczowe poprzedza deklarację, wprowadzone nazwy są aliasami odpowiednich typów

Jak wiele osób wcześniej powiedziało, nie ma potrzeby używania typedefs deklarujących wyliczenia w C ++ . Ale to jest wyjaśnienie składni typedef! Mam nadzieję, że to pomoże (prawdopodobnie nie OP, ponieważ minęło prawie 10 lat, ale każdy, kto stara się zrozumieć tego rodzaju rzeczy).

Francisco Orlandini
źródło
1

W niektórych przewodnikach po kodzie C napisano, że wersja „typedef” jest preferowana ze względu na „przejrzystość” i „prostotę”. Nie zgadzam się, ponieważ typedef zaciemnia prawdziwą naturę zadeklarowanego obiektu. W rzeczywistości nie używam typedefs, ponieważ deklarując zmienną C, chcę mieć jasność co do tego, czym właściwie jest obiekt. Ten wybór pomaga mi szybciej zapamiętywać, co naprawdę robi stary fragment kodu, i pomoże innym, gdy zachowa kod w przyszłości.

AdRiX
źródło
1

Rzeczywista odpowiedź na pytanie „dlaczego” (które jest zaskakująco ignorowane przez istniejące odpowiedzi na górze tego starego pytania) jest taka, że enumdeklaracja ta prawdopodobnie znajduje się w pliku nagłówkowym, który ma być kompatybilny zarówno jako kod C, jak i C ++ (tj. zawarte zarówno w implementacjach C, jak i C ++). Sztuka pisania takich plików nagłówkowych zależy od zdolności autora do wybierania funkcji językowych, które mają odpowiednie kompatybilne znaczenie w obu językach.

Mrówka
źródło