【问题标题】:What to put in a default label of a switch?在交换机的默认标签中添加什么?
【发布时间】:2012-04-28 02:18:43
【问题描述】:

假设我有一个枚举。

enum class ShapeName : char {TRIANGLE,CIRCLE,SQUARE};

后来我有了这样的功能:

void Function (ShapeName const shape){

    switch (shape){
        case ShapeName::TRIANGLE:
            DoSomething1();
            break;

        case ShapeName::CIRCLE:
            DoSomething2();
            break;

        case ShapeName::SQUARE: 
            DoSomething3();
            break;

        default:
            //THIS CODE BLOCK SHOULD NEVER BE EXECUTED!
    }

    return;
}

虽然不应执行默认标签,但我想说明如果程序员向“ShapeName”添加另一个值并且未在切换中考虑的情况下可能出现的潜在错误。
会发生什么你推荐我做?

1.断言
我可以使用断言,但我在断言什么?

assert(false); //?

2。例外情况
我可以抛出异常,但我认为这不是很好的做法。我的印象是,由于某些环境而无法预测的运行时事件会出现异常。

3.退出
我可以立即退出程序并出现错误。 这感觉是最好的主意,但我不确定这是否是好的做法。我认为断言的优点是你可以在准备好发布程序时将它们全部关闭。然后,所有的断言代码都将不复存在。


也许还有另一种我不知道的方式。我确实使用了一个编译器标志来警告未解释的值,但我仍然想知道其他人推荐什么。

【问题讨论】:

  • 4.你能告诉你的编译器警告它吗?这是我期望旧式枚举的一个选项。
  • @SteveJessop g++ -w -Wall -Wextra -Wswitch -Wswitch-default -Wswitch-enum -std=c++0x -o main main.cpp 奇怪,我没有收到警告全部用上面的代码。我添加了另一个 ShapeName 并摆脱了默认标签。还是什么都没有!
  • 老鼠。不过,C++11 的实现还没有完成,所以我们可以满怀希望。

标签: c++ default switch-statement assert


【解决方案1】:

我喜欢用信息丰富的信息进行断言的想法。试试这个:

assert (!"The default case of so-so switch was reached.");

这总是返回 false,但提供了一条您可以使用的消息。

编辑:
我找到了我从记忆中提取这个概念的来源;它在以下书中:
C++ 编码标准 - 101 条规则和指南

【讨论】:

  • 或者如果你不喜欢它的拼写方式,assert(false && "message");
  • 请记住,断言通常从“发布”版本中剥离。
  • 对于发布版本,您还可以使用编译器的 __assume(0) 等效项作为优化提示。
  • 感觉有点像一个聪明的黑客。也感觉是最好的解决方案。感谢source
  • 我喜欢写像assert(not "reachable");这样的东西
【解决方案2】:

我对这种特定情况的偏好,即您打开一个枚举标签并处理所有情况,是省略默认值。这样,对于任何合理的编译器,如果有人将新标签添加到枚举而不是交换机,您将收到有关未在交换机中处理的标签的编译时警告。

【讨论】:

  • 显然 GCC 对于新的 C++11 枚举类是不合理的,尽管提问者没有说 GCC 的版本。
  • @SteveJessop gcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1
  • @SteveJessop: gcc(尝试过 4.4.5、4.5.2 和 4.6.3)如果我删除默认设置并添加 OTHER 标签,则会给我“warning: enumeration value ‘OTHER’ not handled in switch”...
【解决方案3】:

我想这在某种程度上取决于 DoSomething# 方法的作用。

如果稍后它会导致不那么优雅的崩溃,您肯定希望在调用它时中断运行时,并解释“使用无效枚举调用的函数:enumName”或向开发人员通知他们没有更新它的东西switch 语句,而不是让他们想知道以后是否会失败。

【讨论】:

    【解决方案4】:

    简答:使用多态性来消除问题。

    长答案: 解决根本问题(即如何防止缺少条目的 switch 语句?)的最简单方法是避免使用 switch 语句如果有可能忘记条目。 Switch 语句在本地上下文中很有用,但如果它们表示的逻辑在多个位置重复,则可能会忘记更新一个,并且您正在为运行时错误做好准备。

    class Shape
    {
        // functionality MUST be added for new shape types
        // or a compile error will occur
        virtual void DoSomething() const = 0;
    };
    
    void Function (Shape const & shape){
        return shape.DoSomething();
    }
    

    switch 语句在创建站点可能仍然有用:

    enum class ShapeName : char {TRIANGLE,CIRCLE,SQUARE};
    
    unique_ptr< Shape > MakeShape (ShapeName const shape){
        switch (shape){
            case ShapeName::TRIANGLE:
                return unique_ptr< Shape >( new Triangle() );
    
            case ShapeName::CIRCLE:
                return unique_ptr< Shape >( new Circle() );
    
            case ShapeName::SQUARE: 
                return unique_ptr< Shape >( new Square() );
         }
    
         throw std::runtime_error();
         // or whichever option you prefer from the other answers
    }
    

    但这是唯一逻辑存在的地方。甚至可以通过静态多态来改进:

    enum class ShapeName : char {TRIANGLE,CIRCLE,SQUARE,ELLIPSE};
    
    template< ShapeName shape >
    unique_ptr< Shape > MakeShape();
    
    template<>
    unique_ptr< Shape > MakeShape< ShapeName::TRIANGLE >()
    {
        return unique_ptr< Shape >( new Triangle() );
    }
    
    template<>
    unique_ptr< Shape > MakeShape< ShapeName::CIRCLE >()
    {
        return unique_ptr< Shape >( new Circle() );
    }
    
    template<>
    unique_ptr< Shape > MakeShape< ShapeName::SQUARE >()
    {
        return unique_ptr< Shape >( new Square() );
    }
    
    int main()
    {
        MakeShape< ShapeName::TRIANGLE >(); // okay
    
        MakeShape< ShapeName::ELLIPSE >(); // compile error
    }
    

    请参阅Martin Folwer 了解更多信息。

    【讨论】:

    • 为什么要投反对票?我认为对于这个人为的例子,这是最好的解决方案。正是由于这个原因,switch 语句可能是一种代码味道。 default 的问题是你没有得到一个忽略大小写的编译器警告;如果没有default,如果您处于每种情况都返回的那种开关中(例如,工厂),编译器会抱怨在函数结束时没有返回。我认为 MSVC 曾经有一个无法访问的宏来提供帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-04
    • 2013-10-23
    • 1970-01-01
    • 2021-09-23
    • 1970-01-01
    • 2013-12-08
    相关资源
    最近更新 更多