6

我在项目的一部分中有这段代码:

enum myEnum
{
    invalid = -1,
    val1 = 1,
    val2 = 2,
    val3 = 4
};

int bitmask = val1 | val3;

if(bitmask & val1)
    ...
if(bitmask & val2)
    ...
if(bitmask & val3)
    ...

这很好,而且效果很好,但我一直想知道是否可以通过开关来完成。我在想这样的事情:

int checkMask(int& mask)
{
    for(int i = 0; mask; mask &= ~(1 << i++))
    {
        if(mask & (1 << i))
        {
            int ret = mask & (1 << i);
            mask &= ~ret;
            return ret;
        }
    }

    return invalid;
}

#define START_BITMASK_SWITCH(x) int xcopy = x; while(xcopy) { switch(checkMask(xcopy))
#define END_BITMASK_SWITCH };

int bitmask = val1 | val3;

START_BITMASK_SWITCH(bitmask)
{
    case val1:
        ...
        break;
    case val2:
        ...
        break;
    case val3:
        ...
        break;
}
END_BITMASK_SWITCH

所以我的问题是:

  • 我刚刚解决了我的问题吗?我想我有,但它是一个干净的解决方案吗?
  • 有没有更简单的方法来完成这个?
  • 混合#defines 和函数是一个坏主意吗?

  • 4

    7 回答 7

    14

    不,这不是一个干净的解决方案,对于您的上下文,您可以避免混合#define和功能。如果需要,您可以尝试以下解决方案switch()

    int bitmask = val1 | val3;
    int mask = 1;
    while(bitmask)
    {
      switch(bitmask & mask)
      {
      case val1: ... break;
      case val2: ... break;
      case val4: ... break;
      case val8: ... break;
      }
      bitmask &= ~mask; 
      mask <<= 1;
    }
    
    于 2011-07-07T08:24:05.600 回答
    6

    不,它(显然)不是一个干净的解决方案。您的原始代码是直截了当的,没有循环,也没有涉及向语言添加奇怪结构的特殊情况“秘密”宏。

    通过“奇怪的构造”,我的意思是START_BITMASK_SWITCH()/END_BITMASK_SWITCH 宏,它:

    • 在不使用任何标准关键字的情况下添加循环,甚至暗示正在发生循环
    • 当前范围内的 Clobber 名称或多或少静默
    • 包括一个虚假的分号

    您的解决方案没有任何好处,它所做的只是增加膨胀和开销(无论是在代码大小、复杂性和运行时性能方面),只是为了解决出于某种原因想要使用 aswitch来做某事的痒不太适合做。

    显然,这是非常主观的,但你确实问过。

    于 2011-07-07T08:15:47.490 回答
    5

    我看到几个问题:

    • 它增加了没有真正好处的预处理器
    • 它添加了很多慢代码(班次、循环、测试)
    • 它可以防止您添加特殊情况,例如“如果第 2 位打开第 3 位关闭”(if ((bitmask & (val2 | val3)) == val2)
    • 编译器将错过几乎所有优化生成代码的可能性

    它也可以用一种更简单的方式来完成:

    #define START_BITMASK_SWITCH(x) \
        for (uint64_t bit = 1; x >= bit; bit *= 2) if (x & bit) switch (bit)
    
    int bitmask = val1 | val3;
    
    START_BITMASK_SWITCH(bitmask)
    {
        case val1:
            ...
            break;
        case val2:
            ...
            break;
        case val3:
            ...
            break;
    }
    
    于 2011-07-07T08:53:51.757 回答
    3

    如果您愿意,位掩码只是一个布尔数组,而您的枚举是索引。你可以切换一个布尔数组吗?不,你不能,因为它可以同时代表多个状态。您只能像使用任何整数一样切换整个位掩码。

    于 2011-07-07T08:22:45.790 回答
    0

    您可以创建一个 foreach 构造,在其中迭代位掩码的位并提供一个带有 switch 语句的函数。

    于 2011-07-07T08:43:36.910 回答
    0

    当然可以,如果那是你真正想要的。如前所述,只有有限的情况,也许没有,你可能需要。但是你可以。

    #include <iostream>
    
    enum myEnum
    {
        invalid = -1,
        val1 = 1,
        val2 = 2,
        val3 = 4
    };
    
    int main()
    {
        const int bitmask = val1 | val3;
    
        switch (1) {
            case (bitmask & val1) : std::cout << "1"; break;
            case (bitmask & val2) : std::cout << "2"; break;
            case (bitmask & val3) : std::cout << "3"; break;
            default: break;
        }
    }
    
    于 2019-11-16T22:22:38.127 回答
    -1
    enum Positions {
        ALPHA,
        BETA,
        GAMMA
    };
    
    enum Flag {
        ALPHA_FLAG == 1 << ALPHA,
        BETA_FLAG  == 1 << BETA,
        GAMMA_FLAG == 1 << GAMMA
    };
    
    Position position_of (Flag f) {
        unsigned n = f;
        unsigned i = 0;
        while ((n & 1) == 0) {
             ++i;
             n >>= 1;
        }
        return Position (i);
    }
    
    switch (position_of (flag)) {
        case ALPHA:
        case BETA:
        // ...
    };
    

    这对C++0x strong enums更好,然后您可以拥有Position::ALPHAFlag::ALPHA获得更清晰的命名。您还可以使用constexpr安全地位掩码您的标志值。

    于 2011-07-07T08:13:14.413 回答