在C++中声明枚举时,为什么要使用typedef?

220

我已经好多年没有写过C++了,现在我正在尝试重新学习。但是我看到这个问题后,考虑放弃:

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

这是什么?为什么在这里使用了 typedef 关键字?为什么在这个声明中 TokenType 出现了两次?与此有何不同语义:

enum TokenType
{
    blah1 = 0x00000000,
    blah2=0x01000000,
    blah3=0x02000000
};
9个回答

182

在 C 语言中,使用第一种方式声明枚举允许你像这样使用它:

TokenType my_type;
如果您使用第二种方式,您将被迫像这样声明变量:
enum TokenType my_type;

正如其他人所提到的,这对 C++ 没有区别。我猜想,写这篇文章的人可能本质上是 C 程序员,或者你正在将 C 代码编译为 C++。无论哪种方式,都不会影响代码的行为。


18
你的问题只适用于C语言,而不适用于C++。在C++中,枚举和结构体可以直接使用,就好像已经有了typedef一样。 - David Rodríguez - dribeas
8
好的,是的,但这并没有回答实际上被问到的真正问题,那个问题实际上是关于“这到底是什么意思?”的。 - BobbyShaftoe
这到底是一个typedef还是一个枚举? - Miek
6
这个句子的意思是:它既是这样,你也可以说:枚举类型 TokenType_ { ... }; 定义了别名 TokenType。我会将其翻译为:这是一个结合体。你也可以这样说:枚举类型 TokenType_ { ... }; 使用 typedef 定义了 TokenType 别名。 - Ryan Fox
答案是完整的,但我认为重点在于枚举声明后的TokenType;它声明了类型名称。因此,答案不是100%完整的。声明“第一种方法”指定了一个枚举和一个新的类型名称,该枚举是一个语法块中的一部分。所以答案很有帮助,但我认为还可以改进一下。也许我对它的反对票太过严厉了。所以在所有这些之后,我还是给它投了赞成票。如果我真的很确定自己,我会尝试编辑/改进答案...但这是一个相当好的答案。 - Ross Youngblood
我想要补充的是,如果你使用typedef,你不需要给枚举类型命名。例如:typedef enum { blah1 = 0x00000000, blah2 = 0X01000000, blah3 = 0X02000000 } TokenType;这样的代码在大多数C和C++编译器中也可以正常工作。 - mrKirushko

115

这是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 typedef。


1
你所说的在C语言中是正确的。但在C++中则不一定正确。 - Jonathan Leffler
59
我上一句话所说的不就是这个意思吗? - mat
5
@mat,我给你的最后一句话点了赞,但说实话,它措辞不当且令人困惑。 - A.R.

28

你无需这样做。在C语言(而不是C++)中,你需要使用 枚举类型名称 来引用枚举类型的数据元素。为了简化,你可以使用 typedef 将其定义为单一名称的数据类型。

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

允许以枚举类型作为参数定义的函数

void f( MyEnum x )

使用更短的方式

void f( enum MyEnum x )
请注意,类型名称不需要与枚举名称相同。结构也是如此。
另一方面,在C++中,由于可以通过名称直接访问枚举、类和结构体,因此不需要这样做。
// C++
enum MyEnum {
   // ...
};
void f( MyEnum x ); // Correct C++, Error in C

1
实际上,我认为这可能比通常被接受的答案更好,因为它清楚地解释了“为什么”不同。 - Ross Youngblood

14

有些人说C语言没有命名空间,但从技术上讲这不正确。它有三个命名空间:

  1. 标签(enumunionstruct
  2. 标签名
  3. (其他所有东西)

typedef enum { } XYZ;声明一个匿名枚举并将其作为名称为XYZ的全局命名空间导入。

typedef enum ABC { } XYZ;在标签命名空间中声明一个名为ABC的枚举,然后将其导入到全局命名空间中作为XYZ

一些人不想费心使用单独的命名空间,所以他们对所有东西都用了typedef。而其他人则从不使用typedef,因为他们需要命名空间。


1
这并不完全准确。结构体,联合体和枚举通过标记名称引用(除非是匿名的,正如你所提到的)。类型和标记有单独的命名空间。你可以有一个与标记名称完全相同的类型,并且编译可以正常进行。但是,如果枚举与结构体具有相同的标记,这就像两个具有相同标记的结构体或两个枚举一样,会导致编译错误。另外,你忘记了goto语句的标签是单独的命名空间。标签可以与标记或标识符具有相同的名称,但不能与类型具有相同的名称,而标识符可以与标记具有相同的名称,但不能与类型具有相同的名称。 - Rich Jahn

13

在C语言中这样做是好的风格,因为你可以将类型更改为除枚举外的其他类型。

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进行编译。


你是不是想说 在C++中,你可以使用*typedef*来定义枚举类型,以便它能够编译为C++或C语言。?你之前说的是:在C++中,你可以定义枚举类型,以便它能够编译为C++或C语言。 请注意我将你的define改成了typedef。嗯...我想typedef确实也是一种定义方式。 - Gabriel Staples

7

这是从 C 语言中遗留下来的。


不知道“early”限定符是否相关;如果您想在C中使用类型名称而不带枚举前缀,仍然需要编写它。 - Jonathan Leffler
1
我已经很久没有遵循C规范了。我太懒了,没有检查C/C++的区别...这是我的失误。-1分。 - Tim

5

这可能有点老了,但无论如何,我希望您能欣赏到我即将要输入的链接,就像我在今年早些时候发现它时一样。

在这里。当我不得不理解某些令人讨厌的typedef时,我应该引用始终在脑海中的解释:

在变量声明中,引入的名称是相应类型的实例。[...]但是,当typedef关键字出现在声明之前时,引入的名称是相应类型的别名

正如许多人以前所说,在C ++中声明枚举时没有必要使用typedef。但那就是typedef语法的解释! 我希望它有所帮助(也许不是OP,因为已经过去了近10年,但任何正在努力理解这些内容的人都可以受益)。


2
实际上,“为什么”问题的答案(这个老问题的现有答案出奇地忽略了这一点)是,这个“enum”声明可能位于一个头文件中,该头文件旨在作为C和C++代码交叉编译的一部分(即包含在C和C++实现文件中)。编写此类头文件的艺术在于作者选择具有两种语言都有适当兼容含义的语言特性的能力。

1
在一些C代码风格指南中,typedef版本被认为更易于“清晰”和“简单”。我不同意这种观点,因为typedef混淆了所声明对象的真实本质。事实上,我不使用typedef,因为在声明C变量时,我想要清楚地知道对象实际上是什么。这个选择可以帮助我更快地记住旧代码的实际作用,并且将来维护代码时也能帮助他人。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接