【问题标题】:switch may fall through (no it may not)开关可能会通过(不,可能不会)
【发布时间】:2019-08-16 23:12:51
【问题描述】:

在 GCC 7 上,我在 Qt creator 4.9 上启用了大部分警告。现在我有一个涵盖所有枚举值的 switch 语句。如果我添加 default:,我会收到警告(来自 Qt 创建者):

warning: default label in switch which covers all enumeration values

如果我删除 default: 我会收到另一个警告(来自 GCC):

error: this statement may fall through [-Werror=implicit-fallthrough=]  
       }  
       ^

error: all warnings being treated as errors

我该怎么办?关闭警告?它们很有用,我不想关闭它们,但Wimplicit-fallthrough 似乎有问题。

[[fallthrough]] 没有帮助,因为 cases 以 return 结尾,因此我得到(来自 Qt 创建者):

warning: fallthrough annotation in unreachable code

__attribute__ ((fallthrough)) 也没有做任何事情。 /* FALLTHRU */[[gnu::fallthrough]]// fall through 也没有。大概是因为-pedantic

例子:

enum class E {a, b, c};

QVariant fun(E e) {
     switch (e) {
        case E::a: return "something";
        case E::b: return "something_else";
        case E::c: return "something_different";
        // default: return QVariant{};
        // Do I add a default:? What do I add here?
    }
}

希望我尝试过的事情表明我的问题不是 thisthis 或其他类似问题的重复,因为它们不能解决我的问题。

【问题讨论】:

  • 你删除了什么默认值?你能举一个你默认的例子吗?
  • @Ayxan 您应该发布一些实际的可编译代码。这个基于您的示例的 sn-p gcc.godbolt.org/z/qJaiVK 使用 gcc 7 编译而不会发出警告。
  • @M.M 正如我在问题中提到的,它是 GCC 7。我还想注意 IDE,以防万一它有用。
  • 这不是很有帮助,因为 gcc 7 有各种版本。无论如何,您应该发布 Minimal, Reproducible Example 显示您询问的所有各种条件
  • @Ayxan 在该代码中的错误是因为您指定了-ansi 标志(表示-std=c++98),并且您使用了在C++98 中无效的语法enum class。删除该标志,没有错误也没有警告

标签: c++ gcc qt-creator


【解决方案1】:

考虑fun(static_cast<E>(42))。这是一个完美定义的转换,但是您问题中的函数将在没有返回的情况下结束,并且您的程序的行为将是未定义的。这就是为什么 GCC 会警告控制可能会到达非 void 函数的末尾。

那么为什么不添加default 案例呢?考虑一下如果有人返回并向E 添加另一个常量,但忘记更新fun 会发生什么。如果没有default,GCC 会警告您该开关不能处理所有E 的常量。如果您添加 default 案例,那么您就破坏了这种非常有用的保护。

那么正确的做法是什么TM?在函数结束时,在switch 之后返回您的默认值(或throw 或调用abort() 崩溃):

enum class E {a, b, c};

QVariant fun(E e) {
     switch (e) {
        case E::a: return "something";
        case E::b: return "something_else";
        case E::c: return "something_different";
    }
    return "some_default"; // or throw or abort()
}

这让您两全其美。如果有人传递的值不是预定义的枚举器常量之一,它将以明确定义的方式运行。如果有人向E 添加了一个新常量而忘记更新fun,那么编译器仍然会发出警告。

为了进一步讨论,Jason Turner 在他的 CppCon 2018 talk 中讨论了这个话题(以及其他话题),值得一看。

【讨论】:

  • 根据e 的来源,如果e 是未知值,则可以选择失败并中止。用户输入导致死亡是一种非常愚蠢的方法,所以处理错误的输入并继续,但如果程序本身想出了并为e 提供了一些疯狂的值,那么某处就有一个错误,谁知道程序中还有什么被破坏。我认为这是死亡的好时机。
  • 现在是 2020 年,GCC 10 现在认为您的版本也需要关于缺少 default 的警告。
【解决方案2】:

如果我的第一个答案不令人满意,这也许会。这就是我在本地解决问题的方式:

QVariant fun(E e) {
    switch (e) {
        case a: return "something";
        case b: return "something_else";
        case c: return "something_different";
    }
    return "";
}

【讨论】:

  • 这确实解决了问题 (+1) 但我想知道您是如何决定这样做的。
  • Miles 所说的 - 您错过了(假设的)switch 声明失败而没有返回的场景的最终返回声明。
【解决方案3】:

编译器显然与启用的不同警告和内联返回语句混淆。让它开心就好。

enum class E {a, b, c};

QVariant fun(E e) {
     const char* result = "";
     switch (e) {
        case E::a: {
            result = "something"; 
            break;
        }
        case E::b: {
             result = "something_else";
             break;
        }
        case E::c: {
            result = "something_different"; 
            break;
        }
    }
    return result;
}

【讨论】:

  • 嗯,这不会编译,是吗? result 是常量。
  • @Chipster const char *result 是指向 const char 的非常量指针。
  • @HolyBlackCat 你是对的。我只是查找它以刷新我对 const * 与 * const 的记忆。那么,Nvm。
  • 而不是result 类型为const char *,为什么不改成QVariant?这就是函数返回的内容。
【解决方案4】:

如果这一切都存在,为什么还要麻烦一个函数?

目标仅仅是将E::a 的密钥与"something"E::b"something-else" 等配对吗?

结果是否在编译时预设?来自 OP 的 enum 似乎表明密钥在编译时是已知的。

一个简单的数组就足够了。除非我遗漏了什么,否则switch 并没有真正获得任何好处

【讨论】:

  • 实际代码更复杂,有更多switch 语句。这只是重现问题的最小示例。
【解决方案5】:

不可以

当然可以。

相信你的编译器。它比你聪明!

枚举并不是所有可能值的详尽列表。它是域的一些值的一组名称。

演员不仅在这里是可能的;如果您希望提供稳定和健壮的界面,它们是可以预期的。

因此,您要么需要提供default,要么通过您知道(基于它无法猜测的领域知识)的属性或内在函数告诉编译器您知道没有其他可能性会发生。

【讨论】:

  • “相信你的编译器” 很好的答案 (+1) 但并不是我选择不相信我的编译器。只是编译器和 Clang 代码给了我矛盾的警告,一个要求添加 default: 一个要求删除它。接受的答案解决了这两个警告。
猜你喜欢
  • 2022-11-14
  • 2016-01-23
  • 2021-08-26
  • 1970-01-01
  • 2015-11-24
  • 2014-09-02
  • 2012-07-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多