我正在尝试定义一个联合结构,其中一些结构和原始成员在内存中与一个简单的数组重叠。这在 Clang 和 MSVC 中完美运行,但不能用 GCC (G++) 编译。
struct Vector3 {
float x;
float y;
float z;
Vector3() {}
};
struct Plane {
union {
struct {
Vector3 normal;
float d;
};
float elements[4] = { 0 };
};
Plane() {}
};
使用 GCC,我收到此编译错误:
<source>:11:33: error: member 'Vector3 Plane::<unnamed union>::<unnamed struct>::normal' with constructor not allowed in anonymous aggregate
11 | Vector3 normal;
| ^~~~~~
我给出的代码示例是有效的 C++ 吗?为什么在匿名聚合中不允许使用它,但在命名聚合中却似乎可以工作?我可以对其进行哪些更改以使其在 GCC 中工作,而不涉及删除构造函数或在联合中命名结构?它在 Clang 和 MSVC 中有效但在 GCC 中无效的原因是什么?
如果我用
struct {
替换 struct Named {
有没有办法让它工作?
我给出的代码示例是有效的 C++ 吗?
不。不允许使用匿名结构,因此程序格式错误。
它在 Clang 和 MSVC 中工作的原因是什么
当格式错误的程序运行时,通常是由于语言扩展造成的。
但不在海湾合作委员会
类似语言扩展的实现可能存在差异。当然,这种扩展的限制不是由语言定义的。由于此扩展基于 C 语言功能,因此它不一定适用于构造函数等 C++ 功能,这是有道理的。
我可以对其进行哪些更改以使其在 GCC 中工作,而不涉及删除构造函数或在联合中命名结构?
使程序明确定义 C++ 的唯一方法是不使用匿名结构。
额外答案:如果您希望在写信给
elements
或 normal
后阅读 d
,反之亦然,那么这也是不允许的。程序的行为是未定义的。
如何使用重叠内存创建不同名称的属性?除了 Plane 之外,我还想在其他结构中执行此操作,例如通过 3D Basis 结构 columns[3] ,其中数组的成员也可以通过 x、y 和 z 访问。
C++在这方面有局限性,不能用简单的方法来完成。它可以通过依赖运算符重载来完成,但有点复杂:
template<class T, std::size_t size, std::size_t i>
struct Pun {
T a[size];
static_assert(i < size);
auto& operator=(T f) { a[i] = f; return *this; }
operator T&() & { return a[i]; }
operator const T&() const & { return a[i]; }
operator T () && { return a[i]; }
T * operator&() & { return a+i ; }
T const* operator&() const & { return a+i ; }
};
template<class T, std::size_t size>
struct Pun<T, size, size> {
T a[size];
using A = T[size];
operator A&() & { return a; }
operator const A&() const & { return a; }
A * operator&() & { return &a; }
A const* operator&() const & { return &a; }
};
union Plane {
Pun<float, 4, 4> elements;
Pun<float, 4, 0> x;
Pun<float, 4, 1> y;
Pun<float, 4, 2> z;
Pun<float, 4, 3> d;
};
允许读取
Plane
的非活动成员,因为所有元素都是布局兼容的结构。 x
等可以隐式转换为float,elements
可以隐式转换为float数组。