为什么这个联盟显然持有不止一个价值?
Why is this union apparently holding more than one value?
我刚开始使用联合体,对这个测试如何通过感到困惑,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_Event
和SDL_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;
关于在任何给定时间最多只有一名活跃成员的工会,你是对的。
但是标准做了保证,为了方便union的使用(特别是找出,像这里,哪个是active元素):
9.5/1: (...) If a standard-layout union contains several standard-layout structs
that share a common initial sequence, and if an object of this
standard-layout union type contains one of the standard-layout
structs, it is permitted to inspect the common initial sequence of any
of standard-layout struct members;
在您的示例中,SDL_Event
联合有一个联合成员 Uint32 type
,所有 SDL_XXXEvent
结构也以 Uint32
开头。这是通用的初始序列,因此可以使用任何成员对其进行检查(最简单的就是 type
)。
编辑:有趣的评论(从评论中接管)
正如您所指出的,测试不仅是检查:它还使用 event.type
在 type
中写入,然后在 event.key
中分配 keysym
.因此,您想知道活动成员的更改(从 type
到 key
)是否会使公共初始序列无效。
请放心,这绝对有效。 C++ 检查保证确保在 event.type
(公共初始序列)赋值后,event.key.type
也是 SDL_KEYDOWN
。当您仅更改 event.key.keysim
时,没有理由更改 type
的值。
但是请注意 timestamp
、WindowsID
和 event.key
的其他成员处于未定义状态。您的测试不使用它们,因此没有理由失败。但是为了避免这种潜在的问题,更好的方法是构造一个 SDL_KeyboardEvent
,正确初始化它并将整个结构复制到 event.key
如果您仔细观察 SDL_Event
,您会发现它主要是 结构的联合体,其中每个结构都具有相同的初始签名(具有单个 8-位无符号整数作为第一个成员)。
是它起作用的原因。即使联合中的结构具有不同的大小,所有结构都将具有与第一个成员相同的成员 type
。
这是模拟 继承 的简单方法,这意味着 SDL_Event
联合中的所有结构都是彼此的兄弟姐妹。
此外,C 规范明确允许使用联合将多种类型表示为一种 type punning。
但是... 由于问题被标记为 C++ 问题,这在技术上是 未定义的行为.
为了与 C 向后兼容,大多数(如果不是全部)C++ 编译器将允许这样做而不会出现任何抱怨。
我刚开始使用联合体,对这个测试如何通过感到困惑,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_Event
和SDL_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;
关于在任何给定时间最多只有一名活跃成员的工会,你是对的。
但是标准做了保证,为了方便union的使用(特别是找出,像这里,哪个是active元素):
9.5/1: (...) If a standard-layout union contains several standard-layout structs that share a common initial sequence, and if an object of this standard-layout union type contains one of the standard-layout structs, it is permitted to inspect the common initial sequence of any of standard-layout struct members;
在您的示例中,SDL_Event
联合有一个联合成员 Uint32 type
,所有 SDL_XXXEvent
结构也以 Uint32
开头。这是通用的初始序列,因此可以使用任何成员对其进行检查(最简单的就是 type
)。
编辑:有趣的评论(从评论中接管)
正如您所指出的,测试不仅是检查:它还使用 event.type
在 type
中写入,然后在 event.key
中分配 keysym
.因此,您想知道活动成员的更改(从 type
到 key
)是否会使公共初始序列无效。
请放心,这绝对有效。 C++ 检查保证确保在 event.type
(公共初始序列)赋值后,event.key.type
也是 SDL_KEYDOWN
。当您仅更改 event.key.keysim
时,没有理由更改 type
的值。
但是请注意 timestamp
、WindowsID
和 event.key
的其他成员处于未定义状态。您的测试不使用它们,因此没有理由失败。但是为了避免这种潜在的问题,更好的方法是构造一个 SDL_KeyboardEvent
,正确初始化它并将整个结构复制到 event.key
如果您仔细观察 SDL_Event
,您会发现它主要是 结构的联合体,其中每个结构都具有相同的初始签名(具有单个 8-位无符号整数作为第一个成员)。
是它起作用的原因。即使联合中的结构具有不同的大小,所有结构都将具有与第一个成员相同的成员 type
。
这是模拟 继承 的简单方法,这意味着 SDL_Event
联合中的所有结构都是彼此的兄弟姐妹。
此外,C 规范明确允许使用联合将多种类型表示为一种 type punning。
但是... 由于问题被标记为 C++ 问题,这在技术上是 未定义的行为.
为了与 C 向后兼容,大多数(如果不是全部)C++ 编译器将允许这样做而不会出现任何抱怨。