为什么这个联盟显然持有不止一种价值观

Why is this union apparently holding more than one value?

本文关键字:价值观 一种 不止 联盟 为什么      更新时间:2023-10-16

我是使用并集的新手,对如何通过这个测试感到困惑,SDL_Event是一个并集:

TEST(basic_check, test_eq) {
  Dot dot;
  SDL_Event event;            // this is a union, see below
  event.type = SDL_KEYDOWN;   // <= I use one member here
  SDL_Keysym keysym;          // this is a struct
  keysym.sym = SDLK_UP;
  event.key.keysym = keysym;  // <= I use another member here
  dot.handleEvent(event);     // <= but this function accesses value of the first member
  EXPECT_EQ(-Dot::DOT_VEL, dot.getVelY());
}

我的理解是,一个工会只能拥有一种价值观。

然而,在这个测试中,我在联合的一个成员event.type中设置了一个值;然后我更新了联盟的另一个成员event.key。更确切地说,event.key是一个结构,我更新它的成员,使其具有结构SDL_Keysym

这里是函数的代码,然后被调用:

void Dot::handleEvent(SDL_Event& e) {
  if (e.type == SDL_KEYDOWN && e.key.repeat == 0) { //<== access two alternate members?
    switch (e.key.keysym.sym) {   
      case SDLK_UP: 
        velY -= DOT_VEL;
        break;
      case SDLK_DOWN:
        ...   // followed by a lot of other cases 
    }
  }
}

我很困惑,因为if条件访问联合的两个成员(请参阅上面的注释(。我以为它们会是独家的。

作为参考,SDL_EventSDL_KeyboardEvent的定义如下:

typedef union SDL_Event
{
    Uint32 type;                    /**< Event type, shared with all events */
    SDL_CommonEvent common;         /**< Common event data */
    SDL_WindowEvent window;         /**< Window event data */
    SDL_KeyboardEvent key;          /**< Keyboard event data */
    ...            // and a long list of other events 
    ...
} SDL_Event;

typedef struct SDL_KeyboardEvent
{
    Uint32 type;        /**< ::SDL_KEYDOWN or ::SDL_KEYUP */
    Uint32 timestamp;
    Uint32 windowID;    /**< The window with keyboard focus, if any */
    Uint8 state;        /**< ::SDL_PRESSED or ::SDL_RELEASED */
    Uint8 repeat;       /**< Non-zero if this is a key repeat */
    Uint8 padding2;
    Uint8 padding3;
    SDL_Keysym keysym;  /**< The key that was pressed or released */
} SDL_KeyboardEvent;

工会在任何给定时间最多有一名成员是对的。

但该标准提供了一个保证,以便于使用并集(尤其是找出,如这里所示,哪一个是活动元素(:

9.5/1:(…(如果一个标准布局联合包含多个标准布局结构共享一个共同的初始序列,并且如果标准布局联合类型包含一个标准布局structs,允许检查任何标准布局结构成员的;

在您的示例中,SDL_Event并集有一个并集成员Uint32 type,所有SDL_XXXEvent结构也都以Uint32开头。这是常见的初始序列,因此可以使用任何成员对其进行检查(最简单的是type(。

编辑:有趣的备注(取自备注(

正如您所指出的,测试不仅仅是检查:它还使用event.type写入type,然后在event.key中分配keysym。因此,您想知道活动成员(从typekey(的更改是否不会使公共初始序列无效。

请放心,这是完美的。检查的C++保证确保分配event.type(公共初始序列(后,event.key.type也是SDL_KEYDOWN。由于您只更改event.key.keysim,因此type的值没有更改的原因。

然而,注意timestampWindowsIDevent.key的其他成员处于未定义状态。你的测试没有使用它们,所以没有理由失败。但为了避免这种潜在的问题,更好的方法是构造一个SDL_KeyboardEvent,正确初始化它,并将整个结构复制到event.key

如果你仔细观察SDL_Event,你会发现它主要是一个结构的unio,其中每个结构都有相同的初始签名(第一个成员是一个8位无符号整数(。

就是它工作的原因。即使并集中的结构具有不同的大小,所有结构都将具有与第一个成员相同的成员type

这是一种模拟继承的简单方法,这意味着SDL_Event并集中的所有结构都是彼此的兄弟。

此外,C规范明确允许使用并集将多个类型表示为一种类型punning


然而 由于问题被标记为C++问题,从技术上讲,这是未定义的行为。

为了与C向后兼容,大多数(如果不是全部的话(C++编译器都允许这样做,而不会有任何抱怨。

相关文章: