枚举类枚举的实际值是否保持不变/不变?

时间:2013-09-11 22:36:35

标签: c++11 enums

给出不完整服务器的代码,如:

enum class Command : uint32_t {
    LOGIN,
    MESSAGE,
    JOIN_CHANNEL,
    PART_CHANNEL,
    INVALID
};

我可以期望将Command::LOGIN转换为整数会始终给出相同的值吗?

  • 跨编译器?
  • 跨编译器版本?
  • 如果我添加另一个枚举?
  • 如果删除枚举?

转换Command::LOGIN看起来像这样:

uint32_t number = static_cast<uint32_t>(Command::LOGIN);

关于我在这里做什么的一些额外信息。通过将该枚举转换为将其发送到服务器/客户端的整数,将该枚举馈送到线路上。我并不特别关心数字是什么,只要它始终保持不变。如果它不会保持不变,那么显然我将不得不通过通常的方式提供我自己的数字。

现在我的潜在怀疑是它会根据编译代码的编译器而改变,但我想知道。

奖金问题:编译器/语言如何确定Command::LOGIN使用的数字?

在提交这个问题之前,我已经注意到从3137527848到0并返回的一些变化,所以依靠它不会改变显然是无效的。我仍然很好奇这个数字是如何确定的,以及这个数字的变化方式或原因。

3 个答案:

答案 0 :(得分:2)

如果为枚举常量指定显式整数值,则保证在转换为整数类型时始终具有相同的值。

只需执行以下操作:

enum class Command : uint32_t {
    LOGIN = 12,
    MESSAGE = 46,
    JOIN_CHANNEL = 5,
    PART_CHANNEL = 0,
    INVALID = 42
};

如果未明确指定任何值,则隐式设置值,从零开始,每次向下移动列表时增加1。

从草稿 n3485

中引用
  

[dcl.enum]第2段

     

使用枚举仅枚举枚举的枚举类型是   unscoped枚举,其枚举器是未作用域的枚举器。   enum-keys枚举类和枚举结构在语义上是等价的;   使用其中一个声明的枚举类型是作用域   枚举,其枚举器是作用域枚举器。 [...]   枚举器列表中的标识符被声明为常量,并且可以   出现在需要常量的地方。枚举器定义   =为关联的枚举器提供由constant-expression指示的值。如果第一个枚举器没有初始化器,那么   相应常数的值为零。 <强>一种   没有初始值设定项的枚举器定义为枚举器提供了   通过增加前一个枚举器的值得到的值   一个

依赖于此的缺点是,如果列表顺序在将来以某种方式发生变化,那么您的代码可能会默默地中断,因此我建议您明确。

答案 1 :(得分:2)

从C ++ 11标准(或更确切地说,n3485):

[dcl.enum] / 2

  

如果第一个枚举器没有初始值设定项,则相应常量的值为零。没有初始化程序枚举器定义枚举器提供通过增加前一个枚举器的值而获得的值之一。

此外,[expr.static.cast] / 9

  

作用域枚举类型的值可以显式转换为整数类型。如果原始值可以由指定的类型表示,则该值不变。

我认为很明显,枚举数的值可以用uint32_t表示;如果不是,[dcl.enum] / 5说“如果枚举器的初始化值不能用基础类型表示,那么该程序就是格式错误。”

因此,只要您使用基础类型进行转换(显式或通过std::underlying_type<Command>::type),只要您不在它们之前添加任何枚举器,这些枚举数的值就会固定 (在相同的枚举中)或更改其顺序

正如Nicolas Louis Guillemo pointed out,在转移价值时要注意可能存在不同的字节顺序。

答案 2 :(得分:1)

Command :: LOGIN始终为0,只要它是列表中的第一个枚举。请小心其余的枚举,因为根据计算机是使用大端还是小端,它们将具有不同的二进制表示。