无作用域枚举是否仍然有用

Are unscoped enumerations still useful?

本文关键字:有用 是否 作用域 枚举      更新时间:2023-10-16

我在 C++11 标准中没有发现任何说不推荐使用无作用域枚举的措辞,但从实用的角度来看,我想知道它们是否仍然有用。我的团队中的很多人已经养成了将无作用域枚举转换为作用域枚举的习惯,但这引起了一些头痛:

class foo
{
public:
    enum MyEnum { One, Two, Three };
};

他们将其转换为:

class foo
{
public:
    enum class MyEnum { One, Two, Three };
};

这意味着当使用这些枚举器时,而不是foo::One,它看起来像foo::MyEnum::One。我一直在问以下最佳实践:

  1. 如果转换为作用域枚举,请将其移出类并移入全局作用域或命名空间作用域(以提高可用性并避免上述后一个用法示例(。
  2. 如果使枚举保持无作用域,请确保它位于命名空间/类/函数/等作用域中,以便它不会与其他名称冲突。

这两点之间的主要区别在于,对于 #1,我们不会将它们放在类中,否则会增加一些详细的间接性。

所有这些似乎都过于复杂,并且似乎将类中已有的枚举保留为无作用域枚举要简单得多。在两者之间做出决定的一般最佳实践方法是什么?

作用

域枚举器不能隐式转换为其基础类型。 如果需要枚举值隐式转换为其基础类型,则不能使用作用域枚举器。

例如,当您与不受控制的 API 通信并且枚举值是位标志时,这很有用。 需要 uint32_t 或某些其他整型作为位标志的 API(您不控制(。

您可以覆盖 operator| etc 以保持所有内容"类型",或者让它们生成底层类型 - 但enum class的单个元素不能隐式转换为 uint32_t

我觉得无作用域enum的另一个用途是替换#define FOO 32样式宏。 我得到的不是文本替换,而是具有相同含义的令牌,并且我不必重写代码库。 如果有一组紧密分组的此类值,我最终可以更改期望使用enum值传递此类#define令牌的int参数,并且现在键入了参数!

这允许逐步迁移到更好的代码库。

下一步可能是对这些值使用作用域枚举,但必须一次执行所有操作的开销可能意味着可能无法执行第一步。 完美是好的敌人。


另一方面,如果您的枚举实际上只是一组枚举值,并且它们在基础类型中的值并不重要,那么作用域枚举几乎总是比无作用域枚举更好。 它们可以防止意外转换为基础类型:如果基础类型中的值只是一个实现细节,则此类转换可能会导致 bug。

这是迄今为止我发现的最常见的enum用例 - 一个可区分值的列表,其底层类型和值只是一个实现细节。