Почему вы используете typedef при объявлении enum в C ++?

183

Я не писал C ++ годами и сейчас пытаюсь вернуться к нему. Затем я наткнулся на это и подумал о том, чтобы сдаться:

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

Что это? Почему typedefключевое слово используется здесь? Почему имя TokenTypeпоявляется дважды в этой декларации? Чем семантика отличается от этой:

enum TokenType
{
    blah1 = 0x00000000,
    blah2=0x01000000,
    blah3=0x02000000
};
Тим Меррифилд
источник

Ответы:

156

В C объявление вашего enum первым способом позволяет вам использовать его так:

TokenType my_type;

Если вы используете второй стиль, вы будете вынуждены объявить свою переменную следующим образом:

enum TokenType my_type;

Как уже упоминалось, это не имеет значения в C ++. Я предполагаю, что либо человек, который написал это, в глубине души программист на C, либо вы компилируете код на C ++. В любом случае, это не повлияет на поведение вашего кода.

Райан Фокс
источник
12
Ваш вопрос верен только для C, но не для C ++. В C ++ перечисления и структуры могут использоваться напрямую, как если бы был typedef.
Дэвид Родригес - dribeas
7
Ну, да, но это действительно отвечает на реальный вопрос, который был задан и который действительно был о том, «что это вообще значит?».
BobbyShaftoe
Так это технически typedef или enum?
Miek
5
Это оба. Вы также можете сказать: enum TokenType_ {...}; typedef enum TokenType_ TokenType;
Райан Фокс
Ответ полон, но я считаю, что дело в том, что TokenType; после объявления перечисления есть то, что объявляет имя типа. Таким образом, ответ не завершен на 100%. Объявление «первым способом» указывает ОБА и перечисление, а также новое типовое имя, то есть перечисление в одном блоке синтаксиса. Так что ответ был полезным, но я думаю, что можно немного улучшить. Возможно я был слишком резок, чтобы понизить голосование. Так что я проголосовал за все это. Если бы я был действительно уверен в себе, я бы попробовал изменить / улучшить ответ ... но это довольно хороший ответ
Росс Янгблад
97

Это наследие C, в C, если вы делаете:

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

Вы должны будете использовать это, делая что-то вроде:

enum TokenType foo;

Но если вы сделаете это:

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

Вы сможете объявить:

TokenType foo;

Но в C ++ вы можете использовать только первое определение и использовать его, как если бы оно было в определении типа C.

мат
источник
1
То, что вы говорите, верно в Си. Это не так в С ++.
Джонатан Леффлер
49
Разве не то, что я сказал в моем последнем предложении?
мат
2
@mat Я одобрил ваш комментарий о последнем предложении, но, если честно, он плохо сформулирован и запутан.
AR
20

Вам не нужно это делать. В C (не C ++) вы должны были использовать enum Enumname для ссылки на элемент данных перечислимого типа. Чтобы упростить его, вам было разрешено ввести его для одного типа данных.

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

разрешенные функции, принимающие параметр перечисления, должны быть определены как

void f( MyEnum x )

вместо дольше

void f( enum MyEnum x )

Обратите внимание, что имя typename не обязательно должно совпадать с именем enum. То же самое происходит со структурами.

В C ++, с другой стороны, это не требуется, так как перечисления, классы и структуры могут быть доступны напрямую как типы по их именам.

// C++
enum MyEnum {
   // ...
};
void f( MyEnum x ); // Correct C++, Error in C
Дэвид Родригес - дрибеи
источник
На самом деле, я думаю, что это может быть лучшим ответом, чем общепринятый, так как он четко объясняет, «почему» это отличается.
Росс Янгблад
11

В Си это хороший стиль, потому что вы можете изменить тип на что-то, кроме enum.

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

В C ++ вы можете определить enum, чтобы он компилировался как C ++ или C.

Имейте в виду
источник
Разве ты не хочешь сказать In C++ you can *typedef* the enum so that it will compile as C++ or C.? Вы сказали: In C++ you can define the enum so that it will compile as C++ or C.Обратите внимание , как я изменил свой defineTo typedef. Ну ... Я полагаю , typedefING является определяющим.
Габриэль Стейплс
6

Удержание от C.

Тим
источник
Не знаю, что «ранний» квалификатор имеет значение; вы все равно пишете это в C, если хотите использовать имя типа без префикса enum.
Джонатан Леффлер
1
правда. Я удалю это. Я не следовал спецификации C в течение долгого времени. Мне было лень проверять различие с / с ++ ... -1 для меня.
Тим
6

Некоторые люди говорят, что у C нет пространств имен, но это технически неверно. У него есть три:

  1. Теги (enum , unionиstruct )
  2. Этикетки
  3. (все остальное)

typedef enum { } XYZ; объявляет анонимное перечисление и импортирует его в глобальное пространство имен с именем XYZ .

typedef enum ABC { } XYZ; объявляет перечисление с именем ABC в пространстве имен тега, а затем импортирует его в глобальное пространство имен какXYZ .

Некоторые люди не хотят беспокоиться об отдельных пространствах имен, поэтому они вводят все. Другие никогда не вводят typedef, потому что им нужно пространство имен.

russbishop
источник
Это не совсем точно. на структуры, объединения и перечисления ссылаются по имени тега (если не указано иное, как вы упомянули). Существуют отдельные пространства имен для типов и тегов. Вы можете иметь тип с тем же именем, что и тег, и скомпилировать нормально. Однако, если перечисление имеет тот же тег, что и структура, это ошибка компиляции в точности, как если бы 2 структуры или 2 перечисления с одинаковым тегом были бы. Вы также забыли, что ярлыки для goto - это отдельное пространство имен. Метка может быть тем же именем, что и тег, или идентификатором, но не типом, а идентификатор может иметь то же имя, что и тег, но не типом.
Рич Ян
3

Это довольно старо, но в любом случае, я надеюсь, вы оцените ссылку, которую я собираюсь напечатать, так как я оценил ее, когда натолкнулся на нее ранее в этом году.

Вот это . Я должен процитировать объяснение, которое всегда у меня в голове, когда я должен понять некоторые неприятные определения типа:

В объявлениях переменных введенные имена являются экземплярами соответствующих типов. [...] Однако, когда typedefключевое слово предшествует объявлению, введенные имена являются псевдонимами соответствующих типов.

Как уже говорили многие люди, нет необходимости использовать typedefs для объявления перечислений в C ++ . Но это объяснение синтаксиса typedef! Я надеюсь, что это помогает (вероятно, не OP, так как это было почти 10 лет, но любой, кто изо всех сил пытается понять такие вещи).

Франциско Орландини
источник
1

В некоторых руководствах по стилю кода C версия typedef считается предпочтительной для «ясности» и «простоты». Я не согласен, потому что typedef скрывает реальную природу объявленного объекта. На самом деле, я не использую typedefs, потому что при объявлении переменной C я хочу уяснить, что это за объект на самом деле. Этот выбор помогает мне быстрее вспомнить, что на самом деле делает старый фрагмент кода, и поможет другим при поддержке кода в будущем.

AdRiX
источник
1

Фактический ответ на вопрос «почему» (который неожиданно игнорируется существующими ответами поверх этого старого вопроса) заключается в том, что это enumобъявление, вероятно, находится в заголовочном файле, который предназначен для перекрестной компиляции как кода на C, так и на C ++ (т.е. включены в оба варианта реализации C и C ++). Искусство написания таких заголовочных файлов опирается на способность автора выбирать языковые функции, которые имеют подходящее совместимое значение на обоих языках.

Муравей
источник