我发现以下模式在我们公司的代码中相当常用。
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
};
TypeA
、TypeB
和Type_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,
};
程序员本可以使用命名空间,这可能更清晰。