“控制到达非空函数的末尾”,并通过枚举类型完全处理大小写切换

问题描述 投票:0回答:3

为什么即使处理了

type_t
的所有可能值,此代码也会触发“控制到达非 void 函数的末尾”?处理此警告的最佳方法是什么?在开关后面添加
return -1

(已测试代码此处

typedef enum {
    A,
    B
} type_t;

int useType(type_t x) {
    switch (x) {
        case A:
            return 0;
        case B:
            return 1;
    }
}


相关:检测将 int 转换为枚举结果是否会转换为非枚举值

c++ g++ compiler-warnings
3个回答
16
投票

一般来说,

enum
并不具有排他性。例如,有人可以像
useType( (type_t)3 );
这样调用你的函数。 C++14 [dcl.enum]/8 中特别提到了这一点:

可以定义一个具有其任何枚举器未定义的值的枚举。

现在,有一堆规则关于哪些其他值对于其他类型的枚举来说是可能的。

枚举有两类。第一个是固定基础类型,例如

enum type_t : int
,或
enum class type_t
。在这些情况下,基础类型的所有值都是有效的枚举器。

第二个是不固定的基础类型,其中包括 C++11 之前的枚举,例如您的枚举。在这种情况下,关于值的规则可以总结为:计算存储枚举的所有值所需的最小位数;那么任何可用该位数表示的数字都是有效值。


因此 - 在您的具体情况下,单个位可以同时保存值

A
B
,因此
3
对于枚举器来说不是有效值。

但是如果您的枚举是

A,B,C
,那么即使
3
没有具体列出,根据上述规则它也是一个有效值。 (所以我们可以看到几乎所有的枚举都不会具有排他性)。

现在我们需要看看如果有人确实尝试将

3
转换为
type_t
会发生什么情况的规则。转换规则是C++14 [expr.static.cast]/10,它表示生成一个未指定的值。

但是,CWG 第 1766 期 认识到 C++14 文本有缺陷,并将其替换为以下内容:

整型或枚举类型的值可以显式转换为完全枚举类型。如果原始值在枚举值(7.2)的范围内,则该值不变。 否则,行为未定义。

因此,在您的特定情况下,恰好有两个具有值

0
1
的枚举器,除非程序已经触发了未定义的行为,否则不可能有其他值,因此警告可能被视为误报。


要删除警告,请添加一个执行某些操作的

default:
案例。我还建议,为了防御性编程的利益,无论如何,有一个默认情况是个好主意。在实践中,它可能有助于“包含”未定义的行为:如果有人碰巧传递了无效值,那么您可以干净地抛出或中止。


注意:关于警告本身:当且仅当控制流到达函数末尾时,编译器不可能“准确”地发出警告,因为这需要解决暂停问题。 他们倾向于谨慎行事:如果不完全确定,编译器会发出警告,这意味着存在误报。

因此,此警告的存在并不一定表明可执行文件实际上允许进入默认路径。



0
投票
会抑制警告(因此避免编译器特定的扩展)。

© www.soinside.com 2019 - 2024. All rights reserved.