【发布时间】:2019-01-26 15:50:12
【问题描述】:
标准中的脚注暗示任何枚举表达式值都是定义的行为;为什么 Clang 的未定义行为清理程序会标记超出范围的值?
考虑以下程序:
enum A {B = 3, C = 7};
int main() {
A d = static_cast<A>(8);
return d + B;
}
the undefined behavior sanitizer下的输出为:
$ clang++-5.0 -fsanitize=undefined -ggdb3 enum.cc && ./a.out
enum.cc:5:10: runtime error: load of value 8, which is not a valid value for type 'A'
注意,错误不在static_cast上,而是在加法上。当创建 A(但未初始化)然后将值为 8 的 int 将 memcpyied 放入 A 时也是如此 - ubsan 错误在于添加,而不是初始加载。
IIUC,较新的 clang 中的 ubsan 确实在 C++17 模式下的 static_cast 上标记了一个错误。我不知道该模式是否也在memcpy 中发现错误。无论如何,这个问题主要集中在 C++14 上。
报告的错误符合标准的以下部分:
对于基础类型固定的枚举,枚举的值是基础类型的值。否则,枚举的值是可由具有最小范围指数 M 的假设整数类型表示的值,以便可以表示所有枚举数。足以容纳枚举类型的所有值的最小位域的宽度是 M。可以定义一个枚举,其值未由其任何枚举器定义。如果 enumerator-list 为空,则枚举的值就好像该枚举有一个值为 0 的枚举器。100
因此枚举 A 的值是 0 到 7(含),而“范围指数”M 是 3。根据 expr.pre 评估具有值 8 的 A 类型的表达式是未定义的行为:
如果在计算表达式期间,结果未在数学上定义或不在其类型的可表示值范围内,则行为未定义。
但是有一个小问题:footnote from dcl.enum 写着:
这组值用于定义枚举类型的提升和转换语义。 它并不排除枚举类型的表达式具有超出此范围的值。 [强调我的]
问题:如果“[dcl.enum] 不排除枚举类型的表达式具有超出此范围的值”,为什么值为 8 且类型为 A 的表达式未定义行为?
【问题讨论】:
-
它标记了 static_cast。
-
@jbapple:注释不规范。因此,您不能依赖它们来进行明确定义的行为。
-
@supercat:感谢您的评论。举这个具体的例子。 Clang 给出了运行时错误,但 GCC 没有。我查阅了 GCC 手册,据我所知,他们没有记录任何不这样做的理由。那么,我们可以得出结论说 GCC 不是一个高质量的实现吗? GCC 会在此基础上接受错误报告吗?我认为不会。
-
@PW:在标准没有强加任何要求但大多数针对特定目的的实现以一致的方式表现的地方,任何旨在适合该目的但表现不同的质量实现应该记录这样做的充分理由。据我所知,大多数实现将为每个枚举选择一个足够大以容纳其所有值的整数类型,使用该类型存储枚举,并允许将适合该类型的任何值存储在枚举中。在某些情况下,其他行为可能更有用,并且...
-
...如果实现以某种其他方式表现出能够更好地满足其客户需求的行为,那么它不会有任何“错误”,前提是它记录了该行为,并且假设替代行为确实如此满足客户的需求。
标签: c++ enums c++14 language-lawyer undefined-behavior