3

我发现以下代码在调试版本中正常工作,但在发布时不正确:

enum FileFormatDetection
{
    Binary,
    ASCII,
    Auto
};

FileFormatDetection detection_mode = (FileFormatDetection)ReadIntKey(NULL, KEY_ALL, "FIL_ASCIIORBINARY", -1); // Assigns -1, as confirmed by debug output
if (detection_mode < 0 || detection_mode > 2)
{
    // Not a valid value, so set to default:
    detection_mode = Auto;
}

在每个构建中,调试输出确认该值为 -1。在调试版本中,值 -1 导致输入 if-branch;在发布时,它没有。

我尝试将 detection_mode 转换为 int,如下所示:

if ((int)detection_mode < 0 || (int)detection_mode > 2)

和:

if (int(detection_mode) < 0 || int(detection_mode) > 2)

但两者都没有任何区别。

我可以完成这项工作的唯一方法是将枚举变量转换为整数堆栈变量并测试:

int detection_int = (int)detection_mode;
if (detection_int < 0 || detection_int > 2)
{
    ...

现在按预期输入了 if 分支。

我不明白为什么这是必要的——我仍然认为原始代码(或至少测试临时演员)应该有效。谁能解释为什么没有?

4

2 回答 2

10

问题是转换导致未定义的行为,因为您正在为枚举分配一个不是枚举数之一的值。

当您在发布模式下编译时,优化器会看到以下表达式:

if (detection_mode < 0 || detection_mode > 2)

并且知道detection_modeis的类型FileFormatDetection,它知道该特定枚举的所有有效值都在范围内,因此 if 语句永远不可能true(至少在定义明确的程序中),因此它if完全删除了。

强制转换为intinif将无济于事,因为同样的推理方式仍然适用:强制转换之前的值(同样在定义明确的程序中)在范围内[0..2],因此优化器删除了if. 重要的是要注意强制转换是否显式并不重要,因为int在这两种情况下,枚举都将被转换为进行比较。

如果您将值存储在 an 中,int那么一切都会改变。有不少int值超出了该范围,因此必须进行比较。另请注意,通过此更改,程序变得定义良好,因为-1 它是一个有效的int. 这实际上是您应该做的:将值作为 获取int,并且只有当它可以转换为枚举时,才执行转换。

于 2012-07-16T17:22:21.403 回答
6

该变量detection_mode具有 type FileFormatDetection,因此它只能(合法地)包含 0..2 范围内的值。其他任何事情都是未定义的行为。显然,在您的情况下,优化器正在识别这一点,并将测试抑制为小于零。

您应该做的是阅读, 并且仅在范围检查int转换为 :FileFormatDetection

int raw_detection_mode = ReadIntKey(...);
if ( raw_detection_mode <= 0 || raw_detection_mode > 2 ) {
    raw_detection_mode = Auto;
}
FileFormatDetection detection_mode =
    static_cast<FileFormatDetection>( raw_detection_mode );
于 2012-07-16T17:23:54.180 回答