GCC中枚举开关的控制流分析不足

uli*_*tko 10 c++ gcc gcc-warning control-flow

在以下C++代码中:

typedef enum { a, b, c } Test;

int foo(Test test) {
    switch (test) {
        case a: return 0;
        case b: return 1;
        case c: return 0;
    }
}
Run Code Online (Sandbox Code Playgroud)

编译时发出警告-Wall,表示控件到达非void函数的结尾.为什么?


编辑

一般说来test示例中的变量可以包含任何值都是正确的.

foo(12354) 不编译:

> test.cpp:15:14: error: invalid conversion from ‘int’ to ‘Test’
> test.cpp:15:14: error:   initializing argument 1 of ‘int foo(Test)’

因为12354是不是一个有效的Test值(虽然它确实是明文有效Ç,但它不是在C++中).

你肯定可以显式地将任意整数常量强制转换为枚举类型,但是不是认为是未定义的行为吗?

Sol*_*ear 5

问题是类型的变量Test可以具有编译器提供的类型所允许的任何值.因此,如果它确定它是一个32位无符号整数,则允许该范围内的任何值.所以,如果比如你打电话foo(123456),你的switch语句不会捕捉任何价值,有没有return你之后switch.

default在您的交换机中添加一个案例或添加一些错误处理代码.

  • @SolarBear:**不要**在交换机中设置默认值只是为了使警告静音.在没有默认情况下切换枚举是很好的做法,因为这意味着无论何时更新枚举,如果忘记更新开关,都会收到警告.但是,你可以在切换后调用`abort`. (6认同)
  • @Beta:1)如果没有处理开关,警告将是"未在交换机中处理枚举值XXX",但所有声明的值都被处理 - 很好.2)问题中的警告是"控制到达无效函数的结束"...以解决*警告你不应该在交换机上添加默认值,而是在切换后添加适当的编译/流控制语句.(我甚至用g ++ 4.5.2 -Wall验证了这两个方面.) (3认同)
  • @Beta:Tony为我回答,你和我不是在谈论同样的警告:)我有一个宏`UNREACHABLE(XXX)`它需要一个c-string并扩展为`assert(!XXXX)`(在调试中)和`throw Unreachable(XXX)`(发布中).在你的情况下,我会把它放在函数的底部(**开关后**),以便在发出gcc的警告时发出警告,并在达到*时通知.我仍然不会把'默认'声明:) (3认同)
  • @ulidtko:你可以调用`foo(static_cast <Test>(123456));`,这基本上是一样的.考虑将通过网络数据包传入的整数转换回枚举,您*有*执行此类强制转换,如果没有正确的验证,它们*会有害*:/ (2认同)
  • @Beta:Matthieu是正确的(像往常一样)......当前的警告来自函数,不一定是命中'return`语句......修复它,应该有一些抛出,中止,退出,断言,甚至返回(如果一个有意义的运行时垃圾处理行为是有意义的)*在交换机之后,而不是在交换机内,因为这会不必要地阻止编译器验证声明的枚举全部被处理.@David:基本上*是*默认行为,但由于上述原因,它不应该是交换机内部的情况. (2认同)
  • @Tony,@ Matthieu M.:Tony非常好.(我的编译器已经过时了.)我想睡在它上面,但鉴于此,我倾向于同意:在切换之后,所有这些都属于. (2认同)

Pau*_*l R 3

无法保证变量test将包含有效的枚举,因此实际上您有可能到达非 void 函数的末尾,例如,如果您的调用代码如下所示:

Test test = Test(3);
foo(test);
Run Code Online (Sandbox Code Playgroud)

  • @ulidtko:由于 C++ 没有明确的范围检查,因此枚举相对容易包含无效值 - 您应该防御性地编码,以便捕获这种情况,而不是让它只是通过开关,从而从开关返回未定义的值功能。 (2认同)