C++ C++;使用位移位指定枚举显式值

C++ C++;使用位移位指定枚举显式值,c++,enums,openscenegraph,C++,Enums,Openscenegraph,我一直在研究开源项目中的一些代码,并注意到不止一次枚举的值是通过将一个值进行位移位来分配的,并且是递增的。我看不出这样做的任何具体原因,也看不到通过增加+1来分配值的效率有什么提高 不管怎样,如果没有一些代码来证明什么让我感到困惑,这可能没有什么意义 一级 enum EventType { NONE = 0, PUSH = 1<<0, RELEASE =

我一直在研究开源项目中的一些代码,并注意到不止一次枚举的值是通过将一个值进行位移位来分配的,并且是递增的。我看不出这样做的任何具体原因,也看不到通过增加+1来分配值的效率有什么提高

不管怎样,如果没有一些代码来证明什么让我感到困惑,这可能没有什么意义

一级

enum EventType {
        NONE                = 0,
        PUSH                = 1<<0,
        RELEASE             = 1<<1,
        DOUBLECLICK         = 1<<2,
        DRAG                = 1<<3,
        MOVE                = 1<<4,
        KEYDOWN             = 1<<5,
        KEYUP               = 1<<6,
        FRAME               = 1<<7,
        RESIZE              = 1<<8,
        SCROLL              = 1<<9,
        PEN_PRESSURE        = 1<<10,
        PEN_ORIENTATION     = 1<<11,
        PEN_PROXIMITY_ENTER = 1<<12,
        PEN_PROXIMITY_LEAVE = 1<<13,
        CLOSE_WINDOW        = 1<<14,
        QUIT_APPLICATION    = 1<<15,
        USER                = 1<<16
    };

之所以要这样做,是因为可以使用以这种方式定义为位掩码的枚举。给定一个32位整数,您可以有32个唯一的特征,这些特征都可以使用枚举值的逐位组合来启用或禁用

例如,如果您想创建一个事件处理程序,在使用位移位的实现中侦听
QUIT_应用程序
CLOSE_窗口
,只需使用
QUIT_应用程序| CLOSE_窗口
。每个枚举都是一个位,将它们组合在一起可以方便地指定所需的每种类型的事件,如果只是从1-16枚举它们,则无法这样做

考虑这样一种情况,您需要一个事件处理程序,它使用您建议的“更高效”枚举侦听
PUSH
RELEASE
。如果你把它们放在一起,你会得到
3
3
也是示例中双击的值。所以没有办法知道你真正想要的是什么活动;它是由
PUSH
RELEASE
组合而成,还是仅仅是
双击
?这就是为什么为每个枚举类型保留一个位是如此强大的原因

您经常会看到这样定义的枚举值:

enum EventType {
        NONE                = 0,
        PUSH                = 1,
        RELEASE             = 2,
        DOUBLECLICK         = 3,
        DRAG                = 4,
        MOVE                = 5,
        KEYDOWN             = 6,
        KEYUP               = 7,
        FRAME               = 8,
        RESIZE              = 9,
        SCROLL              = 10,
        PEN_PRESSURE        = 11,
        PEN_ORIENTATION     = 12,
        PEN_PROXIMITY_ENTER = 13,
        PEN_PROXIMITY_LEAVE = 14,
        CLOSE_WINDOW        = 15,
        QUIT_APPLICATION    = 16,
        USER                = 17
    };
enum EventType {
    NONE                =       0,
    PUSH                =     0x1,
    RELEASE             =     0x2,
    DOUBLECLICK         =     0x4,
    DRAG                =     0x8,
    MOVE                =    0x10,
    KEYDOWN             =    0x20,
    KEYUP               =    0x40,
    FRAME               =    0x80,
    RESIZE              =   0x100,
    SCROLL              =   0x200,
    PEN_PRESSURE        =   0x400,
    PEN_ORIENTATION     =   0x800,
    PEN_PROXIMITY_ENTER =  0x1000,
    PEN_PROXIMITY_LEAVE =  0x2000,
    CLOSE_WINDOW        =  0x4000,
    QUIT_APPLICATION    =  0x8000,
    USER                = 0x10000
};
有时你甚至会看到这样的事情:

    ...
    CLOSE_WINDOW        =  0x4000,
    QUIT_APPLICATION    =  0x8000,
    CLOSE_AND_QUIT      =  CLOSE_WINDOW | QUIT_APPLICATION,
    ...

位移位表示法更容易阅读(对于某些人来说),它们都做相同的事情。每个枚举值表示一个位或位的离散组合(除了表示没有所有位的
NONE

这样做的通常原因是将每个枚举值与最终v值的一个位相关联,因此您可以(例如)将多个标志编码到单个变量中

例如,对于典型的32位系统,您可以将32个单独的标志编码(显然已经足够了)为单个32位
int
(或者,最好是
unsigned int

例如,如果您正在查看键盘上的键,则可以将字母和数字等“普通”键编码为一个字节(如您所建议的,可能使用连续值),将shift、alt和control等“修改器”键编码为单个位。这将允许您(例如)将control+alt+A之类的内容编码为单个值

同样,对于鼠标消息,您可以将鼠标按钮视为“修改器”,因此可以将拖动鼠标之类的内容编码为单个值


在这两种情况下,将“修饰符”编码为单个位的重要一点是,这允许您以后毫不含糊地检索这些修饰符——如果在值中设置了正确的位,则使用该修饰符。相比之下,如果你只使用连续的数字,你就不能在之后提取单个片段。例如,如果将输入编码为
1
2
3
,则无法判断
3
是用于指示与
3
相对应的原始输入,还是同时指示
1
2
的输入。但是,如果你将这些值编码为<代码> 1 < /C>,<代码> 2 和<代码> 4 >代码>,你可以组合值,并且仍然解码它们,这样你就可以准确地看到输入需要什么来产生特定的值。

< P>这是因为一个东西可以用C++实现的情况之一。没有添加显式实现它的机制

枚举提供链接器/调试器可见常量,并且可以限定为类和模板的范围,因此,尽管它不能完全实现最终用户试图实现的目标,当然也不能显式实现,枚举表达式中的值不必是连续的,这意味着它被认为足以实现枚举位掩码

结果值可直接用于位掩码,例如:

    enum {
        Widget = 1 << 0,  // value b00000001
        Dingo = 1 << 1,   // value b00000010
        Herp = 1 << 2     // value b00000100
    };

    if (flag & Widget)
        doWidgetThings();
    if (flag & Dingo)
        feedTheDog(baby);
    if (flag & Herp)
        win();

这是一个好主意。您可以通过按位or(
|
)组合多个枚举数,并将结果存储在类型为
EventType
的单个对象中。答案很好,非常有意义。我想我从来没有考虑过多个事件同时发生,但支持这样的事情是有道理的。
    enum {
        Widget = 1 << 0,  // value b00000001
        Dingo = 1 << 1,   // value b00000010
        Herp = 1 << 2     // value b00000100
    };

    if (flag & Widget)
        doWidgetThings();
    if (flag & Dingo)
        feedTheDog(baby);
    if (flag & Herp)
        win();
    flag = 0; // matches none
    flag = Herp; // matches one flag.
    flag = Widget | Dingo; // sets flag to have both widget and dingo.