“默认”开关案例会干扰跳转表优化吗?

时间:2010-11-25 15:54:22

标签: c++ switch-statement jump-table

在我的代码中,我习惯于编写包含如下所述断言的后备默认情况,以防止在语义改变时忘记更新开关

switch(mode) {
case ModeA: ... ;
case ModeB: ... ;
case .. /* many of them ... */
default: {
  assert(0 && "Unknown mode!");
  return ADummyValue();
}
};

现在我想知道人为回退检查默认情况是否会干扰跳转表生成?想象一下,“ModeA”和“ModeB”等是连续的,所以编译器可以优化成一个表。由于“默认”情况包含一个实际的“返回”语句(因为断言将在发布模式中消失,并且编译器会抱怨缺少返回语句),编译器似乎不太可能优化默认分支。

处理此问题的最佳方法是什么?有些朋友建议我用空指针取消引用替换“ADummyValue”,以便编译器在存在未定义的行为时可以省略警告缺少返回语句。有没有更好的方法来解决这个问题?

7 个答案:

答案 0 :(得分:3)

至少在我看过的编译器中,答案通常是否定的。他们中的大多数人都会像这样编译一个switch语句,代码大致相当于:

if (mode < modeA || mode > modeLast) {
    assert(0 && "Unknown mode!");
    return ADummyValue();
}
switch(mode) { 
    case modeA: ...;
    case modeB: ...;
    case modeC: ...;
    // ...
    case modeLast: ...;
}

答案 1 :(得分:2)

如果您的编译器是MSVC,则可以使用__assume内在函数:http://msdn.microsoft.com/en-us/library/1b3fsfxw(v=VS.80).aspx

答案 2 :(得分:2)

如果你使用“默认”(ha!)<assert.h>,那么定义与NDEBUG宏相关联,所以也许只是

    case nevermind:
#if !defined(NDEBUG)
    default:
        assert("can" && !"happen");
#endif
    }

答案 3 :(得分:1)

我只看到1个解决方案,以防优化实际受到干扰:臭名昭着的“#ifndef NDEBUG”绕过默认情况。这不是最好的技巧,但在这种情况下很明确。

顺便说一下:你有没看过你的编译器做什么,没有默认情况?

答案 4 :(得分:1)

如果你有一个永远不应该达到的状态,那么你应该杀掉程序,因为它刚刚达到了意想不到的状态,即使在发布模式下(你可能只是更加外交并实际上保存用户数据并完成所有这些操作)在去之前其他好东西。)

除非您实际测量(使用分析器)您需要它们,否则请不要过度使用微观优化。

答案 5 :(得分:1)

处理此问题的最佳方法是禁用断言。这样你也可以留意可能的错误。有时,应用程序崩溃时会有一个好的消息来解释究竟发生了什么,然后继续工作。

答案 6 :(得分:0)

使用编译器扩展:

// assume.hpp
#pragma once

#if defined _MSC_VER
#define MY_ASSUME(e) (__assume(e), (e) ? void() : void())
#elif defined __GNUC__
#define MY_ASSUME(e) ((e) ? void() : __builtin_unreachable())
#else   // defined __GNUC__
#error unknown compiler
#endif  // defined __GNUC__

-

// assert.hpp
#include <cassert>
#include "assume.hpp"

#undef MY_ASSERT
#ifdef NDEBUG
#define MY_ASSERT MY_ASSUME
#else   // NDEBUG
#define MY_ASSERT assert
#endif  // NDEBUG