我发现以下代码在调试版本中正常工作,但在版本中不正确:
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分支;在发布中,它没有。
我已尝试将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分支。
我无法理解为什么这是必要的 - 我仍然认为原始代码(或至少测试演员临时代码)应该有效。谁能解释为什么没有呢?
答案 0 :(得分:10)
问题是当你为枚举中的枚举器之一分配一个值时,强制转换会导致未定义的行为。
在发布模式下编译时,优化器会看到表达式:
if (detection_mode < 0 || detection_mode > 2)
并且知道detection_mode
的类型是FileFormatDetection
,它知道该特定枚举的所有有效值都在范围内,因此if语句永远不能是true
(至少在一个定义良好的程序中)所以它完全删除if
。
在int
中投射到if
无济于事,因为同样的推理仍然适用:投射前的值(再次在定义良好的程序中)在{{1所以优化器会删除[0..2]
。重要的是要注意,演员是否明确并不重要,因为在这两种情况下,枚举将转换为if
进行比较。
如果您将值存储在int
中,那么一切都会发生变化。有相当多的int
值超出该范围,因此必须执行比较。另请注意,通过此更改,程序将得到明确定义,因为int
是有效的-1
。这实际上就是你应该做的事情:将值作为int
获取,并且只有当它可以转换为枚举时,才能执行转换。
答案 1 :(得分:6)
变量detection_mode
的类型为FileFormatDetection
,因此它可以
仅(合法地)包含0..2范围内的值。还有别的
未定义的行为。在你的情况下,显然,optmizer是
认识到这一点,并将测试抑制为小于零。
你应该做的是阅读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 );