【问题标题】:The behavior of value-initializing an enum值初始化枚举的行为
【发布时间】:2015-01-14 02:42:07
【问题描述】:

首先,我想说,根据 cppreference.com,对枚举进行值初始化有点不可能。

根据http://en.cppreference.com/w/cpp/language/value_initialization,值初始化枚举实际上执行零初始化。因此,根据http://en.cppreference.com/w/cpp/language/zero_initialization,对枚举进行零初始化的效果是:

如果T 是标量类型,则对象的初始值是整数常量零隐式转换为T

但是,整数常数零不能隐式转换为枚举。最终,枚举不能进行值初始化。这听起来很奇怪,并且值初始化枚举确实适用于 VC、GCC 和 clang。那么,标准对此有何规定?

第二,根据http://en.cppreference.com/w/cpp/language/static_cast

如果将表达式的值转换为枚举的底层类型,不是目标枚举值之一)

那么,这是否意味着如果目标枚举没有等于 0 的枚举数,则对枚举进行值初始化(如果它确实有效)实际上可能会导致未定义的行为?

【问题讨论】:

  • 回复:第二。这不是标准所说的。从 C++14:“5.2.9/10 整数或枚举类型的值可以显式转换为枚举类型。如果原始值在 范围内,则该值不变枚举值 (7.2)。否则,结果值未指定(并且可能不在该范围内)。强调我的。枚举值的范围(由 7.2/8 定义)使得 0 始终落入其中。
  • @IgorTandetnik cppreference 已更新
  • 零初始化的描述也是错误的。 [dcl.init]/6.1 表示“如果T 是标量类型(3.9),则将对象初始化为通过将整数文字0(零)转换为T 获得的值”。注意缺少“隐式”。 @Cubbi static_cast 的修复仍然关闭;当前描述仅适用于基础类型不固定的枚举。 Per [dcl.enum]/8, “对于基础类型固定的枚举,枚举的值就是基础类型的值。”
  • @T.C.已编辑,谢谢!不要犹豫,自己更正任何内容,它是公开可编辑的。

标签: c++ enums initialization language-lawyer value-initialization


【解决方案1】:

cmets 给出了这个问题的答案。下面给出了我试图解释其背后的整个标准的尝试。

零初始化T 类型的对象或引用意味着:

  • 如果T是标量类型(3.9),对象被初始化为值 通过将整数文字0(零)转换为T获得;

(枚举是标量类型;§3.9/9) 因此,由于转换不是隐式的,我们不是在看第 4 节,而是在第 5.2.9 节;

表达式static_cast<T>(v) 的结果是 将表达式v 转换为类型T

§5.2.9/10 然后定义如何将整数值转换为枚举类型。

整数或枚举类型的值可以显式转换为 枚举类型。 如果原始值是,则值不变 在枚举值范围内(7.2)。否则,结果 值未指定(可能不在该范围内)。

必须证明零在所有枚举的枚举值范围内。
接下来的五个引述取自第 7.2/8 节:

对于基础类型固定的枚举, 枚举是底层类型的值。

由于所有允许的基础类型在其值范围内都包含零*,因此这会自动给出所需的结果。现在,对于没有固定基础类型的枚举,

否则,对于 emin 是最小的枚举 enumerator 和 e max 是最大的, 枚举是 b minb 范围内的值 max ,定义如下:

即我们必须证明 bmin 总是小于或等于零,而 bmax 总是大于或等于零。

K 为 1 表示二 补码表示,0 表示一个补码或 符号大小表示。
b max 是大于等于max(|e min的最小值>| - K, |e max|) 等于 2M - 1,其中 M 是一个非负整数。

|e max| 是非负数,两个数的最大值至少与两个数一样大。因此 max(|e min| - K, |e max|) 也是非负的,并且 bmax 必须大于或等于该数字 - 所以我们的第一个要求得到满足。

b min 如果 emin 是非负数且 -(bmax + K) 否则。

bmin 显然是零或负数:bmax 是非负数,如上所示,并且 K 是非负的(0 或 1),因此它们和的加法逆是非正的。我们的第二个要求得到满足。最后,

如果 enumerator-list 为空,则 枚举就像枚举有一个单一的枚举器 值0

这通过设置 emin = emax = 0 得到上述结果。


  • 这简化为“所有整数类型在其值范围内都为零”的说法,这留给读者来证明。

【讨论】:

    【解决方案2】:

    1:这可能是不可理解的:

    enum class SomeEnum : int { V1 = 0, V2 = 1, V3 = 2 }; 
    SomeEnum a = 0; // compile error
    SomeEnum b = SomeEnum.V1; // OK
    

    这是对未定义行为的基本保护!

    2:是的,是的:)

    SomeEnum c = static_cast<SomeEnum>(1); // = SomeEnum.V2
    SomeEnum d = static_cast<SomeEnum>(5); // undefined behavior
    

    static_cast 根据定义是危险的,它只能用于支持序列化或旧的 c 接口!

    【讨论】:

    • 有趣的是,这编译没有错误并且“工作正常”完全无视正确性:coliru.stacked-crooked.com/a/21a0f0256e412848(使用 GCC 和 Clang 测试)。
    • static_cast&lt;SomeEnum&gt;(5); 不是 UB。 5 在枚举可表示的值中;你甚至明确声明底层类型是int。见:What happens if you static_cast invalid value to enum class?
    • 技术上static_cast&lt;SomeEnum&gt;(5); 是有效的,但从角度来看它不是,因为没有各自的价值。所以从语言的角度来看,行为是 UD,但在实践中是可预测的
    • 不,语言精确地指定了static_cast&lt;SomeEnum&gt;(5) 的行为。您或任何程序员可能不会期望枚举类型的变量与该枚举的所有枚举数比较不相等,但该语言不做出任何此类保证。在没有相应枚举器的情况下使用枚举值的一种方法是转换为基础类型,即static_cast&lt;int&gt;(static_cast&lt;SomeEnum&gt;(x)) == x 对于可以表示为int 的所有值x 都为真(并且语言保证),假设@987654333 @ 是 SomeEnum 的底层类型。
    猜你喜欢
    • 1970-01-01
    • 2013-06-01
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多