【问题标题】:What happens if an enum cannot fit into an unsigned integral type?如果枚举不能适合无符号整数类型会发生什么?
【发布时间】:2017-01-29 23:20:08
【问题描述】:

根据Bathsheba 的要求和"What happens if an enum cannot fit into an integral type?" 的后续问题:

假设枚举定义如下:

enum foo : unsigned int
{
    bar = UINT_MAX,
    oops
};

oops 的值是否已定义?


MSVS2015编译:

warning C4340: 'oops': value wrapped from positive to negative value
warning C4309: 'initializing': truncation of constant value
warning C4369: 'oops':  enumerator value '4294967296' cannot be represented as 'unsigned int', value is '0'

MSVS2015 输出:

bar = 4294967295
oops= 0

gcc 4.9.2 编译:

9 : note: in expansion of macro 'UINT_MAX'
bar = UINT_MAX,
^
10 : error: enumerator value 4294967296l is outside the range of underlying type 'unsigned int'
oops
^
Compilation failed

gcc 4.9.2 输出

//compilation failed

【问题讨论】:

  • 似乎int 会是一个更有趣的问题,因为bar + 1 的计算会导致UB。
  • @KerrekSB:这基本上是我对链接问题的回答的症结所在。
  • 哼。 UINT_MAX + 1 显然是一个编译时可评估的常量表达式,但鉴于它不适合 unsigned,C++ 标准希望编译器选择 long long 作为枚举类型,但不能因为你已经强迫它成为unsigned。对我来说,这解释了编译器错误,甚至可能是答案。标准中的“如果基础类型是固定的,则可以将值转换为其提升的基础类型”中的行我认为也与它有关。
  • @Bathsheba 刚刚测试了一下,如果你不限制类型,你只会收到警告:coliru.stacked-crooked.com/a/a8306b64279b700c
  • [dcl.enum]/2 和 7 在这里似乎是相关的。 “如果枚举器的初始化值不能由底层类型表示,则程序格式错误。” 很有趣,但我不确定它是否适用。目前正试图在那里建立标准。

标签: c++ types enums language-lawyer


【解决方案1】:

这是一个非常有趣的问题。简单的答案是这实际上是未定义的:标准没有说明这种情况。

为了有一个更好的例子,考虑这个enum

 enum foo : bool { True=true, undefined };

按照标准:

[dcl.enum]/2: [...] 一个没有初始化器枚举器定义 > 给 enumerator 一个通过将 前一个 enumerator 的值加一而获得的值。

因此,在我们的示例中foo::undefined 的值为2 (true+1)。不能表示为bool

格式不正确吗?

,根据标准,它是完全有效的,只有不固定的底层类型有不能表示所有枚举值的限制:

[dcl.enum]/7:对于底层类型不固定的枚举,[...] 如果没有整数类型可以表示所有枚举值, 枚举格式不正确。

它没有说明 固定的底层类型,它不能代表所有的枚举值。

原题的oopsundefined的值是多少?

它是未定义:标准没有说明这个案例。

foo::undefined 的可能值:

  • 可能的最大值 (true):undefinedoops 应该是基础类型的最大值。
  • 最低可能值 (false):基础类型的最小值。注意:在有符号整数中,它与整数溢出的当前行为(未定义行为)不匹配。
  • 随机值 (?):编译器会选择一个值。

所有这些值的问题在于它可能导致两个字段具有相同的值(例如foo::True == foo::undefined)。

初始化器(例如undefined=2)和“隐式”初始化器(例如True=true, undefined)的区别

按照标准:

[dcl.enum]/5:如果底层类型是固定的,则右大括号之前的每个枚举数的类型都是底层类型 和 enumerator-definition 中的 constant-expression 应为基础类型的转换常量表达式。

换句话说:

 enum bar : bool { undefined=2 };

等价于

 enum bar : bool { undefined=static_cast<bool>(2) };

然后bar::undefined 将是true。在“隐式”初始化程序中,情况并非如此:这个标准段落只说初始化程序,而不是“隐式”初始化程序。

总结

  1. 通过这种方式,具有 fixed-underlying-typeenum 可能具有无法表示的值。
  2. 标准未定义它们的值。

根据问题和 cmets,这在 GCC 和 clang 中无效,但对 MSVS-2015 有效(带有警告)。

【讨论】:

  • 我喜欢你的回答,但如果底层类型是无符号的,它真的是未定义的吗?您已经引用了[dcl.enum]/7yourself,这应该(因为溢出是为无符号类型设计的)导致超出范围的值首先再次变为 0...
  • 我也不确定bool 是这里的最佳选择,因为基础数据既没有签名也没有无符号,也不会溢出(AFAIK),这是一个非常特殊的情况。跨度>
  • @SimonKraemer 我和你一样想,但你应该记住,枚举器值的类型与其基础类型不同。换句话说,标准应该规定一个枚举值应该被转换成它的 *underlying-type(参见The difference between initializer (e.g. undefined=2) and "implicit" initializer (e.g. True=true, undefined) 部分)。
  • 感谢您的澄清。
  • @SimonKraemer 只是为了好玩:很明显,在 not-fixed underlying type enum 的情况下,它的格式不正确 - 使用常规的 unsigned int:标准说明了这一点在 [dcl.enum]/7 中明确显示。
猜你喜欢
  • 2017-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-04
  • 2014-09-30
相关资源
最近更新 更多