C++长长的 switch 语句还是用地图查找?

发布于 2024-08-25 04:13:35 字数 327 浏览 9 评论 0原文

在我的 C++ 应用程序中,我有一些值充当代表其他值的代码。为了翻译代码,我一直在争论使用 switch 语句还是 stl 映射。开关看起来像这样:

int code;
int value;
switch(code)
{
case 1:
    value = 10;
    break;
case 2:
    value = 15;
    break;
}

映射将是一个 stl::map 并且翻译将是一个简单的查找,并将代码用作键值。

哪一个更好/更高效/更清洁/被接受?为什么?

In my C++ application, I have some values that act as codes to represent other values. To translate the codes, I've been debating between using a switch statement or an stl map. The switch would look something like this:

int code;
int value;
switch(code)
{
case 1:
    value = 10;
    break;
case 2:
    value = 15;
    break;
}

The map would be an stl::map<int, int> and translation would be a simple lookup with the code used as the key value.

Which one is better/more efficient/cleaner/accepted? Why?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

等你爱我 2024-09-01 04:13:35

就我个人而言,我会使用映射,因为它的使用意味着数据查找 - 使用开关通常表示程序行为的差异。此外,使用映射修改数据映射比使用开关更容易。

如果性能是一个真正的问题,那么分析是获得可用答案的唯一方法。如果分支错误预测发生得足够频繁,则切换可能不会更快。

考虑这个问题的另一种方法是,将代码和关联值组合到数据结构中是否更有意义,特别是如果代码和值的范围是静态的:

struct Code { int code; int value; };

Code c = ...

std::cout << "Code " << c.code << ", value " << c.value << std::end;

Personally, I would use the map, as its use implies a data lookup - using a switch usually indicates a difference in program behavior. Furthermore modifying the data mapping is easier with a map than with a switch.

If performance is a real issue, profiling is the only way to get a usable answer. A switch may not be faster if branch mispredictions happen often enough.

Another approach to think about this is if it wouldn't make more sense to combine the code and the associated value into a datastructure, especially if the range of codes and values is static:

struct Code { int code; int value; };

Code c = ...

std::cout << "Code " << c.code << ", value " << c.value << std::end;
阳光的暖冬 2024-09-01 04:13:35

如果你的代码足够连续并且它们的范围允许,你会更好地使用老式的简单数组,像

int lookup[] = {-1, 10, 15, -1 222};

这样的 switch 语句可以简单地重写为

value = Lookup[code];

所有其他选项都会在一定程度上带来额外成本。

If your codes are contiguous enough and their range permit you would be much better of with old-fashioned straightforward array, something like

int lookup[] = {-1, 10, 15, -1 222};

then the switch statement can be rewritten as simply as

value = lookup[code];

all other options introduce additional cost to some extent.

薄凉少年不暖心 2024-09-01 04:13:35

这更取决于代码是什么以及有多少。好的编译器有各种技巧来优化 switch 语句,其中一些技巧不会直接用于 if/then 语句。大多数人都足够聪明,可以做简单的数学运算,或者使用查找/跳转表来处理案例 0、1、2 或案例 3、6、9。

当然,有些不会,而且许多很容易被不寻常或不规则的值集所挫败。此外,如果处理多个案例的代码看起来非常相似,则剪切和粘贴可能会导致维护问题。如果您有许多代码,但可以通过算法将它们分成组,那么您可能会考虑多个/嵌套的 switch 语句,例如,而不是:

switch (code) {
    case 0x0001: ...
    case 0x0002: ...
    ...
    case 0x8001: ...
    case 0x8002: ...
    ...
}

您可能会使用:

if (code & 0x8000) {
    code &= ~0x8000;
    switch (code) {
        case 0x0001: ... // actually 0x8001
        case 0x0002: ... // actually 0x8002
        ...
    }
}
else {
    switch (code) {
        case 0x0001: ...
        case 0x0002: ...
        ...
    }
}

许多语言解释器以这种方式解码操作码,因为单字节操作码可能包含附加信息成各种位,并且转录所有可能的组合及其处理程序将是重复且脆弱的。另一方面,过多的位重整可能会破坏编译器的任何优化并产生反作用。

除非您确定这是一个真正的性能瓶颈,否则我会避免过早的优化:以您认为合理且易于实施的方式进行优化。如果您的应用程序运行速度太慢,请对其进行分析并进行相应优化。

It rather depends on what the codes are and how many there are. Good compilers have various tricks they use to optimise switch statements, some of which they won't employ with straight if/then statements. Most are bright enough to do simple maths or use lookup/jump tables for case 0, 1, 2 or case 3, 6, 9 for example.

Of course some don't, and many are easily foiled by unusual or irregular sets of values. Also if code for handling several cases looks remarkably similar, cut and paste can lead to maintenance issues. If you have many codes but they can be divided algorithmically into groups, you might consider several/nested switch statements, for example rather than:

switch (code) {
    case 0x0001: ...
    case 0x0002: ...
    ...
    case 0x8001: ...
    case 0x8002: ...
    ...
}

You might use:

if (code & 0x8000) {
    code &= ~0x8000;
    switch (code) {
        case 0x0001: ... // actually 0x8001
        case 0x0002: ... // actually 0x8002
        ...
    }
}
else {
    switch (code) {
        case 0x0001: ...
        case 0x0002: ...
        ...
    }
}

Many language interpreters decode opcodes this way, since a single byte opcode may have additional information packed into various bits, and transcribing all possible combinations and their handlers would be repetitious and fragile. On the other hand, excessive bit mangling can defeat any optimisation by the compiler and be counter-productive.

Unless you're sure this is a real performance bottleneck I'd avoid premature optimisation: do it whichever way strikes you as reasonably robust and quick to implement. As and if your application is running too slowly, profile it and optimise accordingly.

橘味果▽酱 2024-09-01 04:13:35

switch 语句会更快,但如果这不是您的应用程序性能瓶颈,您不应该真正关心它。

寻找让你的代码从长远来看更容易维护的东西。

您的样本太短,无法在这方面做出任何有意义的判断。

The switch statement would be faster but if this is not in your applications performance bottleneck you should not really care about that.

Go for what makes your code easier to maintain over the long run.

Your sample is too short to make any meaningful call in that regard.

别闹i 2024-09-01 04:13:35

我自己偏爱查找表,因为在我看来,异常长的 switch 语句似乎混淆了代码和数据之间的分离。我还认为表格更适合未来的更改和维护。

当然,恕我直言。

I'm partial to lookup tables myself, because unusually long switch statements seem to me to confuse a separation between code and data. I also think tables lend themselves better to future changes and maintenance.

All IMHO, of course.

浪漫人生路 2024-09-01 04:13:35

我建议使用静态、常量的对表。这是查找表的另一种形式:

struct Table_Entry
{
    int code;
    int value;
};

static const Table_Entry  lookup_table[] =
{
  {1, 10},
  {2, 15},
  {3, 13},
};

static const unsigned int NUM_TABLE_ENTRIES =
    sizeof(lookup_table) / sizeof(lookup_table[0]);

这样做的好处是该表是在编译时生成的,与必须在运行时初始化的 std::map 不同。如果数量很大,您可以使用 std::lower_bound 来查找条目,前提是表格是有序的。

另一个好处是该技术是数据驱动的。数据可以在不改变搜索引擎的情况下改变。代码或流程的更改可能需要严格的回归测试,但数据更改可能不需要; YMMV。

这与编译器可能生成的内容类似。

I suggest using a static, constant, table of pairs. This is another form of the look up table:

struct Table_Entry
{
    int code;
    int value;
};

static const Table_Entry  lookup_table[] =
{
  {1, 10},
  {2, 15},
  {3, 13},
};

static const unsigned int NUM_TABLE_ENTRIES =
    sizeof(lookup_table) / sizeof(lookup_table[0]);

A benefit to this is that the table is generated at compile time, unlike a std::map which must be initialized during run-time. If the quantities are large, you could use std::lower_bound to find the entry, provided the table is ordered.

Another benefit is this technique is data driven. The data can change without changes to the search engine. Changes to code or process may require serious regression testing but data changes may not; YMMV.

This is similar to what a compiler might generate.

以酷 2024-09-01 04:13:35

如果您可以使用 tr1,您可以使用 unordered_map 来散列值(散列整数也可以非常快),这应该使大多数查找时间恒定。

但是,除非您有分析数据表明这是程序中的瓶颈,否则请采用从设计角度来看最有意义的方法对其进行编码。

If you can use tr1 you could use unordered_map to hash the values (hashing ints can be really fast too), which should make most lookups constant time.

However unless you have profiling data to indicate that this is a bottleneck in your program, code it in the approach that makes the most sense from a design standpoint.

菩提树下叶撕阳。 2024-09-01 04:13:35

如果你所做的只是分配一个值,我会说映射。我的原因是它可以在不更改代码的情况下进行扩展,而您的 switch 语句则不然。

顺便说一句,枚举怎么样?

I say map if all you are doing is assigning a value. My reason is that it is extensible without changing code, which your switch statement is not.

btw, how about an enum?

没有心的人 2024-09-01 04:13:35

我认为,如果“代码”数量变大,则 switch-case 结构的生成代码可能会变得相当大,在这种情况下,我认为 stl::map 更合适。

I think the generated code of the switch-case structure can grow quite large, if the amount of "codes" becomes large, in which case I think the stl::map is more appropriate.

小忆控 2024-09-01 04:13:35

通常我会建议使用映射或查找数组,甚至可能是一些混合查找怪物(当然,假设您正在优化速度或代码大小而不是可读性),但值得注意的是,新版本的 GCC 很智能足以替换像这样的 switch/case 分配来查找表。虽然这对于完全稀疏的密钥来说并不是那么好,但如果您的密钥像这样成团,则可能会这样:
0, 1, 2, 3, 4, 5, 11, 12, 13, 14, 15, 193, 194, 195, 196, 197, 198...

当然,如果你可能喜欢一些算术来进行转换,甚至更好(通常)。在做这样的事情时,永远不要忽视移位、交换字节序或更传统的数学。

Normally I'd suggest a map or lookup array or maybe even some hybrid lookup monster (assuming that you're optimizing for speed or code size rather than readability, of course), but it is worth noting that the new versions of GCC are smart enough to replace switch/case assignments like this to lookup tables. While this isn't so great for totally sparse keys it may be if you're keys are in clumps like:
0, 1, 2, 3, 4, 5, 11, 12, 13, 14, 15, 193, 194, 195, 196, 197, 198...

Of course if you could possibly fond some arithmatic to do the conversion, even better (usually). Never overlook shifting, swapping endianness, or more traditional math when doing something like this.

So尛奶瓶 2024-09-01 04:13:35

unordered_map 可能更适合这里,因为它使用哈希表,并且查找会比 switch 更快。

unordered_map probably will be better suited as here as it is using hash table and lookup will be really faster instead of switch.

我很坚强 2024-09-01 04:13:35
  • 将整数读入数组/向量
  • 对数组/向量进行排序
  • 在底层数组上使用 bsearch
  • Read the integers into an array/vector
  • sort the array/vector
  • use bsearch on the underlying array
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文