相同类型和相同变量的联合在 GCC 中抛出错误,但在 Visual C++ 中不会

Unions of same type and same variables throw error in GCC, but does not in Visual C++

我的头文件中声明了以下代码:

//Integer Vector4D stuffs

typedef union {
    size_t data[4];
    struct {
        size_t x;
        size_t y;
        size_t z;
        size_t w;
    };
    struct {
        size_t x;
        size_t y;
        size_t width;
        size_t height;
    };
} Vector4Di;

//End Integer Vector4D stuffs

我正在为 Windows 和 Linux 编译代码。我正在使用 Windows 10 Pro 和 WSL。

这是我从 Microsoft Visual Studio 2017 获取的编译生成输出:

1>------ Build started: Project: SDL, Configuration: Debug x64 ------
1>C:main_pc.cpp
1>1 File(s) copied
1>block.cpp
1>common.cpp
1>draw.cpp
1>game.cpp
1>input.cpp
1>main.cpp
1>Generating Code...
1>SDL.vcxproj -> C:\Users\tom_mai78101\Documents\VSProjects\SDL\x64\Debug\SDL.exe
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

根据上面的build log,说明编译时build已经成功

但是,相同的源代码会在 GCC 中引发错误:

$ make
clean ...
build ...
main.cpp
In file included from /mnt/c/Users/tom_mai78101/Documents/VSProjects/SDL/SDL/../SDL/main.cpp:1:0:
/mnt/c/Users/tom_mai78101/Documents/VSProjects/SDL/SDL/../SDL/game/common.h: At global scope:
/mnt/c/Users/tom_mai78101/Documents/VSProjects/SDL/SDL/../SDL/game/common.h:358:10: error: redeclaration of 'size_t <unnamed union>::<unnamed struct>::x'
   size_t x;
          ^
/mnt/c/Users/tom_mai78101/Documents/VSProjects/SDL/SDL/../SDL/game/common.h:352:10: note: previous declaration 'size_t <unnamed union>::<unnamed struct>::x'
   size_t x;
          ^
/mnt/c/Users/tom_mai78101/Documents/VSProjects/SDL/SDL/../SDL/game/common.h:359:10: error: redeclaration of 'size_t <unnamed union>::<unnamed struct>::y'
   size_t y;
          ^
/mnt/c/Users/tom_mai78101/Documents/VSProjects/SDL/SDL/../SDL/game/common.h:353:10: note: previous declaration 'size_t <unnamed union>::<unnamed struct>::y'
   size_t y;
          ^

要点是,在 GCC 中,它会抱怨 size_t x;size_t y; 如何在联合内重新声明,Vector4Di。但是 MSBuild 不会抛出这方面的错误,它成功编译了代码。

据我所知,我认为具有相同类型和相同变量名的联合不应该相互冲突,尤其是当变量位于结构中时。特别是在为 C++11 编译时,众所周知 C++11 对联合有更好的支持。

请问为什么GCC报错?我应该如何修复此错误,以便 MSBuild 和 GCC 都能成功编译?

提前致谢。

好的,这是我今天学到的东西。那就是 C++ 不能很好地处理匿名结构/联合。根据评论,我应该在结构和联合中添加标签名称。

所以这是新的修改版本:

//Integer Vector4D stuffs

typedef union {
    size_t data[4];
    struct quaternion {
        size_t x;
        size_t y;
        size_t z;
        size_t w;
    } quaternion;
    struct rect {
        size_t x;
        size_t y;
        size_t width;
        size_t height;
    } rect;
} Vector4Di;

//End Integer Vector4D stuffs

现在,Visual Studio和GCC编译同一个项目和源代码都很好,不再抛出重声明同名同类型变量的错误。我也不需要修改其余的代码来适应新的变化,尽管这个 Vector4Di 结构是在 headers 中声明的,所以这是一个很好的修复。

感谢 MatteoItalia 的帮助。

C++ 标准不允许匿名 struct 不被用于在 union 中定义其类型的成员;使用匿名 struct 将其成员注入封闭的 union is just a widespread extension,但这不是标准的 C++,因此严格来说 well-defined 这种机制是如何工作的。

特别是,您正踏入危险的水域,因为您有两个匿名结构,其成员具有相同的名称;从 C++ 标准的角度来看,这绝对是模糊的,因为当你这样做时 "active union member" 是什么是模棱两可的,例如v4d.x = 5;.

VC++ documentation does not describe what happens in this case; a quick check tells me that it doesn't get mad even if the names refer to conflicting memory locations - 它似乎指的是第一个被定义的匿名结构的成员,如果你问我,这完全是疯了。

gcc 反而会生气,因为这就像在同一个 struct 中声明两个具有相同名称的成员; its documentation 实际上是说:

You must never create such structures that cause ambiguous field definitions. For example, in this structure:

struct {
  int a;
  struct {
    int a;
  };
} foo;

it is ambiguous which a is being referred to with foo.a. The compiler gives errors for such constructs.

因此,这不是您可以通过编译器开关或其他方式解决的问题; gcc 不支持它,期间。

这一愿景得到了唯一正式正式描述此功能的标准的确认,即 ISO C 11 标准;在那里,在 §6.7.2.1 ¶13,据说:

An unnamed member of structure type with no tag is called an anonymous structure; an unnamed member of union type with no tag is called an anonymous union. The members of an anonymous structure or union are considered to be members of the containing structure or union. This applies recursively if the containing structure or union is also anonymous.

并且结构的命名成员必须具有唯一的名称。


所以,回顾一下:

  • 如果你想让它成为标准的 C++,你必须给你的 sub-structures 起一些名字;你可以这样做:

    union Vector4Di {
        size_t data[4];
        struct {
            size_t x;
            size_t y;
            size_t z;
            size_t w;
        } q;
        struct {
            size_t x;
            size_t y;
            size_t width;
            size_t height;
        } r;
    };
    

    调整其余代码并对其感到满意。

  • 如果您可以使用这些扩展,您仍然可以通过在第二个 struct 中以不同的方式命名 xy 来使其以某种方式工作,如在:

    union Vector4Di {
        size_t data[4];
        struct {
            size_t x;
            size_t y;
            size_t z;
            size_t w;
        };
        struct {
            size_t dummy_x;
            size_t dummy_y;
            size_t width;
            size_t height;
        };
    };
    

    在这种情况下,您可以自由地使用 xy 以及 widthheight,我怀疑它甚至还可以 standard-wise - 如果我们忽略 "anonymous struct" 东西;这两个结构是 standard-layout 和 layout-compatible,因此标准保证您可以使用您喜欢的任何名称检查 "common initial sequence"。