如果将无效值 static_cast 到枚举类会发生什么?

2024-03-29

考虑这个 C++ 代码:

enum class Color : char { red = 0x1, yellow = 0x2 }
// ...
char *data = ReadFile();
Color color = static_cast<Color>(data[0]);

假设data[0]实际上是100,根据标准颜色设置为多少? 特别是,如果我以后这样做

switch (color) {
    // ... red and yellow cases omitted
    default:
        // handle error
        break;
}

该标准是否保证会发生违约?如果不是,那么检查错误的正确、最有效、最优雅的方法是什么?标准是否对此做出任何保证,但使用普通枚举?


根据标准颜色设置为多少?

引用 C++11 和 C++14 标准来回答:

[expr.static.cast]/10

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

我们来查一下枚举值的范围: [dcl.enum]/7

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

CWG 1766 之前(C++11、C++14)因此,对于data[0] == 100,结果值被指定(*),并且没有未定义行为 (UB) https://stackoverflow.com/q/2766731/420683参与。更一般地,当您从基础类型转换为枚举类型时,没有值data[0]可以导致 UBstatic_cast.

CWG 1766 (C++17) 之后 See CWG 缺陷 1766 http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1766。 [expr.static.cast]p10 段落已经得到加强,所以你现在can如果将枚举可表示范围之外的值转换为枚举类型,则调用 UB。这仍然不适用于问题中的场景,因为data[0]是枚举的基础类型(见上文)。

请注意,CWG 1766 被认为是标准中的缺陷,因此编译器实现者接受将其应用于其 C++11 和 C++14 编译模式。

(*) char is required to be at least 8 bit wide, but isn't required to be unsigned. The maximum value storable is required to be at least 127 per Annex E of the C99 Standard.


与 [expr]/4 比较

如果在计算表达式期间,结果未在数学上定义或不在其类型的可表示值范围内,则行为未定义。

在 CWG 1766 之前,转换整型 -> 枚举类型可以产生一个未指定值。问题是:未指定的值是否可以超出其类型的可表示值?我相信答案是no——如果答案是yes,在“此操作产生未指定的值”和“此操作具有未定义的行为”之间,对有符号类型的操作获得的保证不会有任何差异。

因此,在 CWG 1766 之前,即使static_cast<Color>(10000) would not调用 UB;但在 CWG 1766 之后,does调用 UB。


现在switch陈述:

[stmt.开关]/2

条件必须是整型、枚举类型或类类型。 [...]进行积分促销。

[会议舞会]/4

的纯右值unscoped基础类型固定的枚举类型 (7.2) 可以转换为其基础类型的纯右值。此外,如果整数提升可以应用于其基础类型,则基础类型固定的无作用域枚举类型的纯右值也可以转换为提升的基础类型的纯右值。

Note: The underlying type of a scoped enum w/o enum-base is int. For unscoped enums the underlying type is implementation-defined, but shall not be larger than int if int can contain the values of all enumerators.

For an 无作用域枚举,这导致我们 /1

除以下整数类型的纯右值bool, char16_t, char32_t, or wchar_t其整数转换等级 (4.13) 小于int可以转换为类型的纯右值int if int可以表示源类型的所有值;否则,源纯右值可以转换为类型的纯右值unsigned int.

如果是unscoped枚举,我们将处理int在这里。为了scoped枚举(enum class and enum struct),不适用积分促销。无论如何,积分提升也不会导致 UB,因为存储的值在基础类型的范围内并且在int.

[stmt.开关]/5

当。。。的时候switch执行语句时,会评估其条件并与每个 case 常量进行比较。如果其中一个 case 常量等于条件的值,则控制权将传递给匹配的后面的语句case标签。如果不case常量匹配条件,如果有default标签,控制传递到由标签标记的语句default label.

The default应该打标签。

Note: One could take another look at the comparison operator, but it is not explicitly used in the referred "comparison". In fact, there's no hint it would introduce UB for scoped or unscoped enums in our case.


作为奖励,该标准是否对此做出任何保证,但使用普通枚举?

无论是否enum范围在这里没有任何区别。但是,基础类型是否固定确实会产生影响。完整的 [decl.enum]/7 是:

For an enumeration whose underlying type is fixed, the values of the enumeration are the values of the underlying type. Otherwise, for an enumeration where emin is the smallest enumerator and emax is the largest, the values of the enumeration are the values in the range bmin to bmax, defined as follows: Let K be 1 for a two's complement representation and 0 for a one's complement or sign-magnitude representation. bmax is the smallest value greater than or equal to max(|emin| − K, |emax|) and equal to 2M − 1, where M is a non-negative integer. bmin is zero if emin is non-negative and −(bmax + K) otherwise.

我们来看看下面的枚举:

enum ColorUnfixed /* no fixed underlying type */
{
    red = 0x1,
    yellow = 0x2
}

请注意,我们不能将其定义为作用域枚举,因为所有作用域枚举都有固定的基础类型。

Fortunately, ColorUnfixed's smallest enumerator is red = 0x1, so max(|emin| − K, |emax|) is equal to |emax| in any case, which is yellow = 0x2. The smallest value greater or equal to 2, which is equal to 2M - 1 for a positive integer M is 3 (22 - 1). (I think the intent is to allow the range to extent in 1-bit-steps.) It follows that bmax is 3 and bmin

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如果将无效值 static_cast 到枚举类会发生什么? 的相关文章

  • 为什么libc++的shared_ptr实现使用完整内存屏障而不是宽松内存屏障?

    在boost的实现中shared ptr 它用放松内存排序以增加其引用计数 https github com boostorg smart ptr blob master include boost smart ptr detail sp
  • asp.net 文本框文本模式数字,仅允许数字

    我只是想知道 ASP NET 中是否有一种方法只允许文本框中的数字textmode number 当我使用这个时
  • 我的线程图像生成应用程序如何将其数据传输到 GUI?

    Mandelbrot 生成器的缓慢多精度实现 线程化 使用 POSIX 线程 Gtk 图形用户界面 我有点失落了 这是我第一次尝试编写线程程序 我实际上并没有尝试转换它的单线程版本 只是尝试实现基本框架 到目前为止它是如何工作的简要描述 M
  • 为什么大多数 C 开发人员使用 Define 而不是 const? [复制]

    这个问题在这里已经有答案了 在许多程序中 define与常量具有相同的用途 例如 define FIELD WIDTH 10 const int fieldWidth 10 我通常认为第一种形式优于另一种形式 它依赖于预处理器来处理基本上是
  • C++:重写已弃用的虚拟方法时出现弃用警告

    我有一个纯虚拟类 它有一个纯虚拟方法 应该是const 但不幸的是不是 该接口位于库中 并且该类由单独项目中的其他几个类继承 我正在尝试使用这个方法const不会破坏兼容性 至少在一段时间内 但我找不到在非常量方法重载时产生警告的方法 以下
  • Clang 编译器 (x86):80 位长双精度

    我正在尝试在 x86 Windows 平台上使用本机 80 位长双精度 海湾合作委员会选项 mlong double 80 https gcc gnu org onlinedocs gcc x86 Options html似乎不适用于 cl
  • 构造函数中显式关键字的使用

    我试图了解 C 中显式关键字的用法 并查看了这个问题C 中的explicit关键字是什么意思 https stackoverflow com questions 121162 但是 那里列出的示例 实际上是前两个答案 对于用法并不是很清楚
  • 从多个类访问串行端口

    我正在尝试使用串行端口在 arduino 和 C 程序之间进行通信 我对 C 编程有点陌生 该程序有多种用户控制形式 每一个都需要访问串口来发送数据 我需要做的就是从每个类的主窗体中写入串行端口 我了解如何设置和写入串行端口 这是我的 Fo
  • 访问者和模板化虚拟方法

    在一个典型的实现中Visitor模式 该类必须考虑基类的所有变体 后代 在许多情况下 访问者中的相同方法内容应用于不同的方法 在这种情况下 模板化的虚拟方法是理想的选择 但目前这是不允许的 那么 模板化方法可以用来解析父类的虚方法吗 鉴于
  • 检查算术运算中的溢出情况[重复]

    这个问题在这里已经有答案了 可能的重复 检测 C C 中整数溢出的最佳方法 https stackoverflow com questions 199333 best way to detect integer overflow in c
  • 通过 NHibernate 进行查询,无需 N+1 - 包含示例

    我有一个 N 1 问题 我不知道如何解决它 可以在这个问题的底部找到完全可重复的样本 因此 如果您愿意 请创建数据库 设置 NUnit 测试和所有附带的类 并尝试在本地消除 N 1 这是我遇到的真实问题的匿名版本 众所周知 这段代码对于帮助
  • 将构建日期放入“关于”框中

    我有一个带有 关于 框的 C WinForms 应用程序 我使用以下方法将版本号放入 关于 框中 FileVersionInfo GetVersionInfo Assembly GetExecutingAssembly Location F
  • 在 C 中使用 GNU automake 中的解析器

    我是 GNU autotools 的新手 在我的项目中使用了 lex 和 yacc 解析器 将它们作为 makefile am 中的源代码会产生以下错误 配置 in AC CHECK PROGS YACC bison yacc none i
  • 如何挤出平面 2D 网格并赋予其深度

    我有一组共面 连接的三角形 即二维网格 现在我需要将其在 z 轴上挤出几个单位 网格由一组顶点定义 渲染器通过与三角形数组匹配来理解这些顶点 网格示例 顶点 0 0 0 10 0 0 10 10 0 0 10 0 所以这里我们有一个二维正方
  • 将代码拆分为标头/源文件

    我从 Asio 的示例页面中获取了以下代码 class tcp connection public boost enable shared from this
  • 结构体指针的动态数组

    我必须使用以下代码块来完成学校作业 严格不进行任何修改 typedef struct char firstName char lastName int id float mark pStudentRecord pStudentRecord
  • 剪贴板在 .NET 3.5 和 4 中的行为有所不同,但为什么呢?

    我们最近将一个非常大的项目从 NET Framework 3 5 升级到 4 最初一切似乎都工作正常 但现在复制粘贴操作开始出现错误 我已经成功制作了一个小型的可复制应用程序 它显示了 NET 3 5 和 4 中的不同行为 我还找到了一种解
  • 什么是 __declspec 以及何时需要使用它?

    我见过这样的例子 declspec在我正在阅读的代码中 它是什么 我什么时候需要使用这个构造 这是 Microsoft 对 C 语言的特定扩展 它允许您使用存储类信息来赋予类型或函数属性 文档 declspec C https learn
  • 转到定义:“无法导航到插入符号下的符号。”

    这个问题的答案是社区努力 help privileges edit community wiki 编辑现有答案以改进这篇文章 目前不接受新的答案或互动 我今天突然开始在我的项目中遇到一个问题 单击 转到定义 会出现一个奇怪的错误 无法导航到
  • 使用 C# 从 DateTime 获取日期

    愚蠢的问题 给定日期时间中的日期 我知道它是星期二 例如我如何知道它的 tue 2 和 mon 1 等 Thanks 您正在寻找星期几 http msdn microsoft com en us library system datetim

随机推荐