c++ - 使用嵌套结构初始化 union

标签 c++ c unions porting

我正在将 C99 代码移植到 C++(14 或 17),并且在许多地方使用了列表初始化程序。现在我遇到编译错误,想知道初始化由结构嵌套的 union 的最简单方法。例如,以下 C 代码片段工作正常:

#include <stdint.h>

typedef union Word_t
{
    uint32_t word32Bits;
    struct
    {
        uint16_t leastSignificant16Bits;
        uint16_t mostSignificant16Bits;
    };
} Word_t;

int main()
{
    Word_t w1 = (Word_t) {.word32Bits = 0x1234ABCD};
    printf("%x\n", w1.word32Bits);

    Word_t w2 = (Word_t) {.mostSignificant16Bits = 0x1234, .leastSignificant16Bits = 0xABCD};
    printf("%x\n", w2.word32Bits);

    return 0;
}


$ gcc test.c --std=c99 -o a && ./a
1234abcd
1234abcd

但是,在 C++ 中它无法编译:

#include <stdint.h>

typedef union Word_t
{
    uint32_t word32Bits;
    struct
    {
        uint16_t leastSignificant16Bits;
        uint16_t mostSignificant16Bits;
    } _word16Bits;
} Word_t;

int main()
{
    Word_t w1 = (Word_t) {.word32Bits = 0x1234ABCD};
    printf("%x\n", w1.word32Bits);

    Word_t w2 = (Word_t) {.mostSignificant16Bits = 0x1234, .leastSignificant16Bits = 0xABCD};
    printf("%x\n", w2.word32Bits);

    return 0;
}


```bash
$ g++ test.c --std=c++14 -o a && ./a
test.c: In function ‘int main()’:
test.c:57:92: error: ‘Word_t’ has no non-static data member named ‘mostSignificant16Bits’
     Word_t w2 = (Word_t) {.mostSignificant16Bits = 0x1234, .leastSignificant16Bits = 0xABCD};

我找到的解决方案是零初始化,然后按如下方式设置结构的内部值:


int main()
{
    Word_t w1 = (Word_t) {.word32Bits = 0x1234ABCD};
    printf("%x\n", w1.word32Bits);

    <del>Word_t w2 = (Word_t) {.mostSignificant16Bits = 0x1234, .leastSignificant16Bits = 0xABCD};
</del>

    Word_t w2 = {0};
    w2._word16Bits = {0x1234, 0xABCD};

    return 0;
}

哪个有效,但它不允许我明确地说 .mostSignificant16Bits = 0x1234例如——我认为这很有用,特别是在阅读代码时。

我尝试了一些方法,例如定义静态成员、创建用户定义的构造函数,但仍然不知道如何简化我将要做的重构。理想情况下,我想将变量声明保持原样 Word_t w2 = (Word_t) {.mostSignificant16Bits = 0x1234, .leastSignificant16Bits = 0xABCD}而所有更改都在 Word_t 的定义中完成.

最佳答案

语法问题

Designated initializers在聚合初始化中,正式成为 C++20 standard 的一部分.

然而,与 C99 相比,它们具有严重的限制:

  • 它们必须以与声明相同的顺序出现;
  • 所有指定元素必须是聚合的直接成员;
  • 只有在初始化器嵌套时才可以嵌套

在您的情况下,可以编译以下内容,但它无法提供您期望从显式命名中获得的灵 active 优势:

Word_t w2 = (Word_t) {._word16Bits  { .leastSignificant16Bits = 0xABCD, .mostSignificant16Bits = 0x1234} };

更严重的问题

首先,这段代码,如果它能工作的话,是不可移植的:它假设little endianness。的目标架构。

其次,这在这里很重要,C++ 对 union 有很强的约束。鉴于对象生命周期的一致性,这些都是必要的。特别是:

[class.union]/1: In a union, at most one of the non-static data members can be active at any time, that is, the value of at most one of the non-static data members can be stored in a union at any time.

因此,如果您构建的 union 中有一个成员处于事件状态(在您的初始化程序中使用的成员),则另一个成员不活动,您不应该访问它。唯一可预见的异常(exception)不适用于您的情况:

[ Note: One special guarantee is made in order to simplify the use of unions: 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; — end note ]

标准还给出了改变 union 活跃成员的方式提示:

[ Note: In general, one must use explicit destructor calls and placement new operators to change the active member of a union. — end note ]

这是针对简单标量类型说的,它可以像您期望的那样在大多数主流编译器上编译和工作。

但重点是,您对 union 的使用与标准不兼容。它是 UB,即使它现在适用于某些实现,但在每个新的编译器版本中,您都无法保证它会继续工作,从而使您的所有投资都面临风险。

为什么是时候改变方法了?

与 C++ 相比,C99 对 union 的限制更少。但是当另一个 union 成员被设置时,它也没有给出关于一个 union 成员可能读取的值的坚定保证:

6.2.6.1/7: When a value is stored in a member of an object of union type, the bytes of the object representation that do not correspond to that member but do correspond to other members take unspecified values.

附件 C99/J 中提到, union 的这种使用是一种未指定的行为,可能会产生可移植性问题。

关于c++ - 使用嵌套结构初始化 union ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58735640/

相关文章:

c - 如何从 C 中的函数返回多个值?

c++ - union 成员中的模板 - 声明不声明任何内容

c++ - 从父节点中删除子节点 - PugiXML

C程序不等待用户输入值

c++ - DebugActiveProcessStop 使正在调试的应用程序崩溃

无法将 union 成员指定为 NULL

c++ - "unsigned :(number)"和 union

c++ - 对象指针和超出范围的对象的 vector

c++ - 向 Qt 项目添加状态对话框

C++程序在输出整数时异常退出