在我的代码中,我习惯于编写包含如下所述断言的后备默认情况,以防止在语义改变时忘记更新开关
switch(mode) {
case ModeA: ... ;
case ModeB: ... ;
case .. /* many of them ... */
default: {
assert(0 && "Unknown mode!");
return ADummyValue();
}
};
现在我想知道人为回退检查默认情况是否会干扰跳转表生成?想象一下,“ModeA”和“ModeB”等是连续的,所以编译器可以优化成一个表。由于“默认”情况包含一个实际的“返回”语句(因为断言将在发布模式中消失,并且编译器会抱怨缺少返回语句),编译器似乎不太可能优化默认分支。
处理此问题的最佳方法是什么?有些朋友建议我用空指针取消引用替换“ADummyValue”,以便编译器在存在未定义的行为时可以省略警告缺少返回语句。有没有更好的方法来解决这个问题?
答案 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