为什么要在结构中放置枚举,然后使用 typedef 名称?

Why put an enum in a struct and then use a typedef name?

本文关键字:然后 typedef 名称 枚举 结构 为什么      更新时间:2023-10-16

我发现以下模式在我们公司的代码中相当常用。

struct Foo
{
enum FType
{
TypeA,
TypeB,
Type_MAX
};
};
typedef Foo::FType FooType;
[...]
FooType m_type;

我的问题是,这样做有什么好处?(或者,这避免了什么问题?需要明确的是,我想知道为什么他们不只是...

enum FooType
{
TypeA,
TypeB,
Type_MAX
};
[...]
FooType m_type;

我不能问原来的程序员,因为他们已经被重新分配了,事实证明,我们公司指定的主题专家实际上是我。

如果有帮助,此代码已在不同时间使用许多版本的 MSVC、gcc 和 clang 针对不同的目标平台进行编译......所有 C++11 之前。

谁能明白为什么要这样做? 提前道歉,如果答案是微不足道的。

编辑以添加:这在类内部使用。(当枚举是全局的时,我们的样式指南要求条目以通用前缀开头,以便将它们与其他符号区分开来。

普通旧枚举的感知问题是枚举器成为定义枚举的作用域中的名称。因此,您可以说,例如,

FooType m_type;
m_type = TypeA;

如果您还定义了类名TypeA则会发生冲突。将枚举放在类中意味着您必须使用作用域限定符来获取名称,这将消除冲突。这也意味着你必须写

FooType m_type;
m_type = FooType::TypeA;

因为以前的版本无效。

此问题的较新解决方案是作用域枚举:

enum class FooType {
TypeA,
TypeB,
Type_MAX
};

现在你可以说

FooType m_type;
m_type = FooType::TypeA;

但不是

m_type = TypeA;

正如@Jarod42指出的,这里的一个区别是,由普通枚举定义的枚举器可以隐式转换为int,而作用域枚举中的枚举器则不能。因此,对于类中的定义,

int i = FooType::TypeA;

有效,i获取值 0。对于作用域枚举,它是无效的。

在这两种情况下,强制转换都可以进行转换:

int i = static_cast<int>(FooType::TypeA);

这样做很可能是为了防止污染enum成员的全局范围。 当你有

enum FooType
{
TypeA,
TypeB,
Type_MAX
};

TypeATypeBType_MAX成为全局范围内的名称。 这可能会导致与其他enum或仅与已在使用的其他名称发生冲突。 通过将enum放在struct可以将名称限制为结构的作用域。 实现此目的的另一种方法是使用namespace.

C++11 提供了将enum成员的范围限定为enum本身的enum class,因此如果您可以处理enum class施加的更严格的控制,则不再需要这样做。

编写此代码时可能没有C++enum class,因此这是避免外部命名空间污染的唯一解决方案。

结构在这里更像是一个命名空间。作者仍然可能希望枚举本身的名称位于外部命名空间中,这是由typedef完成的。

在较旧的C++版本中,枚举名称将添加到周围的命名空间中。

在您的第二个示例中,我可以执行以下操作:

m_type = TypeA;

在原始示例中,您需要执行以下操作:

m_type = FooType::TypeA;

如果 TypeA 在您的应用程序中相当常见,我可以理解为什么您不想污染它周围的命名空间。

你应该总是把enum放在C++11之前的struct(或class)中。这样做的原因是它可以防止全局namespace的污染,并且在使用结构的成员时强制使用限定名称。即Foo::TypeA.为什么?因为否则其他人可能会决定在代码中的其他地方创建名为TypeA的常量或另一个enum成员,并且会出现名称冲突。

typedef可能只是为了方便不要每次都键入完全限定的enum类型名称。

这仅适用于 C++11 之前。C++11 具有声明作用域枚举的enum class。你提到这段代码是在那之前写的。

我的问题是,这样做有什么好处?(或者,这避免了什么问题?

它将单个枚举标识符限制在类的命名空间中:

int TypeA;
struct Foo
{
enum FType
{
TypeA, // OK, Foo::TypeA does not conflict with ::TypeA
TypeB,
Type_MAX,
};
};
enum FooType
{
TypeA, // not OK, conflicts with the existing ::TypeA declaration
TypeB,
Type_MAX,
};

程序员本可以使用命名空间,这可能更清晰。