为什么 enum class int 类型的值不能用作 int

why can enum class values of type int not be used as int

我想将旧式 enum 更改为 enum class : int 因为它有自己的范围。

但是编译器抱怨在整数算术中使用这些值。 但为什么 - 因为枚举被显式键入为 int?

示例:

  enum class MsgType : int {
    Output = 1,
    Input  = 2,
    Debug  = 3
  };
  enum class MsgSender : int {
    A = 1,
    B = 2,
    C = 3
  };


  // later in code
  int id = (MsgType::Input << 16) + (MsgSender::A);

产生

error C2678: binary '<<': no operator found which takes a left-hand operand of type 'MyClass::MsgType' (or there is no acceptable conversion)

这对我来说似乎有点不合逻辑。

编辑:
我完全知道施放它们的可能性。但是如果我不希望它们是可转换的,为什么我要将类型指定为 int。特别是如果语法暗示某种“继承自 int

这就是功能。作用域枚举不能隐式转换为整数,因此不能使用它们代替整数。它们在设计上是强类型的。如果您想要隐式转换,请使用无范围枚举。

enum MsgType : int {
  Output = 1,
  Input  = 2,
  Debug  = 3
};
enum MsgSender : int {
  A = 1,
  B = 2,
  C = 3
};

基础类型的作用域和规范是正交的。

或者,如果您只想定义一些操作,而枚举通常保持强类型,您可以重载适当的运算符来实现

int operator<<(MsgType, int); // etc

But if I would not want them to be convertible, why would i specify the type to be int

保证一定的布局。遵循特定的 ABI。允许向前声明类型。

类型安全是使用新作用域枚举的主要原因(也许名称有点误导)。如果你只想要一个有范围的枚举并且可以隐式转换为整数,你可以将它包装在结构或命名空间中:

struct MsgSender {
   enum Values {
      A = 1,
      B = 2,
      C = 3
   };
};

缺点是类型现在是 MsgSender::Values,但值是 MsgSender::A

对于范围枚举,您必须 static_caststd::underlying_type<MsgSener> 才能获得整数。

您可以通过 static_cast.

显式转换它们
  int id = (static_cast<uint32_t>(MsgType::Input) << 16) + (static_cast<uint32_t>(MsgSender::A)) ;

您不能将 c++11 作用域枚举直接用作 int,但可以将其转换为 int

主要是因为类型安全的原因,unscoped enum 可以在 enums 内部泄漏名称,而 scoped enum 没有泄漏名称的风险,名称只能在内部可见。

目前c++中有2种枚举。

C++98 风格的无作用域枚举: enum Color {Red, Green, Yellow};, 可以隐式转换为 int.

C++11 作用域枚举:enum class{Red, Green, Yellow};, 不能隐式转换为int,只能使用强制转换为其他类型,如static_cast<int>(my_color).

与非作用域枚举相比,作用域枚举有 3 个优势:

  1. 减少命名空间污染;
  2. 强类型安全;
  3. 范围内的枚举可以前向声明,非范围内的枚举需要额外的工作才能前向声明;

同时,无范围枚举比范围枚举更灵活。

作用域枚举和非作用域枚举都支持底层类型的规范,作用域枚举的默认底层类型是int。 Unscoped enums 没有默认的底层类型,依赖于编译器,可以是 charint,根据值的范围。

enum class Color: std::uint8_t{Red, Green, Yellow}; // Definition

enum Color: std::uint8_t{Red, Green, Yellow}; // Forward Declaration

仅当声明指定基础类型时,才可以前向声明无作用域的枚举。

在实践中,比起无范围枚举,更喜欢有范围的枚举。

参见 Scott Meyers 所著的“Effective Modern C++”一书,第 10 项:优先使用作用域枚举而不是非作用域枚举。