在我的第一次代码评审中(不久前),有人告诉我,在所有switch语句中包含一个default子句是一种很好的实践。我最近想起了这个建议,但不记得理由是什么了。现在我听起来很奇怪。

是否有一个合理的理由总是包含默认语句? 这是语言依赖吗?我不记得当时我在用什么语言了——也许这适用于一些语言而不是其他语言?


当前回答

如果你知道switch语句只有一组严格定义的标签或值,那么就这样做,以覆盖基础,这样你总是会得到有效的结果。只需将默认值放在标签之上,从编程/逻辑上来说,它将是其他值的最佳处理程序。

switch(ResponseValue)
{
    default:
    case No:
        return false;
    case Yes;
        return true;
}

其他回答

您应该有一个默认值来捕捉传入的非预期值。

然而,我不同意Adrian Smith关于默认的错误消息应该是完全没有意义的。可能会有一个你没有预见到的未处理的情况(这是一点),你的用户最终会看到,像“无法到达”这样的消息是完全没有意义的,在这种情况下对任何人都没有帮助。

举个例子,你有多少次经历过毫无意义的蓝皮书?或者致命异常@ 0x352FBB3C32342?

在我的公司,我们为航空电子设备和国防市场编写软件,我们总是包含一个默认语句,因为switch语句中的所有情况都必须显式处理(即使它只是一个说“什么都不做”的注释)。我们不能让软件只是行为不端,或者仅仅是在意外值(甚至是我们认为不可能的值)上崩溃。

可以讨论的是,默认情况并不总是必要的,但通过总是需要它,我们的代码分析人员很容易检查它。

在某些情况下,没有默认情况实际上是有益的。

If your switch cases are enums values, by not having a default case, you can get a compiler warning if you are missing any cases. That way, if new enum values are added in the future and you forget to add cases for these values in the switch, you can find out about the problem at compile time. You should still make sure the code takes appropriate action for unhandled values, in case an invalid value was cast to the enum type. So this may work best for simple cases where you can return within the enum case rather than break.

enum SomeEnum
{
    ENUM_1,
    ENUM_2,
    // More ENUM values may be added in future
};

int foo(SomeEnum value)
{
    switch (value)
    {
    case ENUM_1:
        return 1;
    case ENUM_2:
        return 2;
    }
    // handle invalid values here
    return 0;
 }

一些(过时的)指南是这么说的,比如MISRA C:

最后一个默认子句的要求是防御性编程。该条款应采取适当的行动,或包含关于为什么不采取行动的适当评论。

这个建议已经过时了,因为它不是基于目前的相关标准。明显的疏漏是哈兰·卡斯勒说的

省略默认大小写允许编译器在看到未处理的大小写时选择性地发出警告或失败。静态可验证性毕竟比任何动态检查都要好,因此当您也需要动态检查时,这种牺牲是不值得的。

正如Harlan还演示的那样,在切换之后可以重新创建与默认情况相同的功能。当每个情况都是早期返回时,这是微不足道的。

从广义上讲,动态检查的典型需求是输入处理。如果一个值来自程序控制之外,它就不可信。

这也是Misra采取极端防御性编程立场的地方,即只要一个无效值在物理上是可表示的,就必须检查它,无论程序是否可证明是正确的。如果软件需要在出现硬件错误时尽可能地可靠,这是有意义的。但正如Ophir Yoktan所说,大多数软件最好不要“处理”错误。后一种做法有时被称为进攻性编程。

我相信这是特定于语言的,对于c++来说是枚举类类型的一个次要问题。这似乎比传统的C enum更安全。但

如果你看一下std::byte的实现,它是这样的:

enum class byte : unsigned char {} ;

来源:https://en.cppreference.com/w/cpp/language/enum

再想想这个:

否则,如果T是一个枚举类型,它的作用域是或 具有固定底层类型的无作用域,如果带括号的init-list具有 只有一个初始化式,如果从初始化式转换为 基础类型是非窄化的,如果初始化为 -直接列表初始化,然后使用 将初始化式转换为其基础类型的结果。 (因为c++ 17)

来源:https://en.cppreference.com/w/cpp/language/list_initialization

这是一个枚举类的例子,表示的值不是定义的枚举值。由于这个原因,您不能完全信任枚举。根据应用的不同,这一点可能很重要。

然而,我真的很喜欢@Harlan Kassler在他的帖子里说的话,我自己也会在某些情况下开始使用这个策略。

这是一个不安全枚举类的例子:

enum class Numbers : unsigned
{
    One = 1u,
    Two = 2u
};

int main()
{
    Numbers zero{ 0u };
    return 0;
}