我试图定义一个联合结构与一些结构和基本成员重叠的内存与一个简单的数组。这在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 Named {
代替struct {
,有办法使它工作吗?
我给出的代码示例有效的c++ ?
。匿名结构是不允许的,所以程序是病态的。
它在Clang和MSVC中工作的原因是什么
当一个格式错误的程序工作时,通常是由于语言扩展。
但不在GCC
类似语言扩展的实现可能存在差异。当然,这种扩展的限制不是由语言定义的。由于这个扩展是基于C语言的特性,所以它不一定能与c++的特性(如构造函数)一起工作,这是有道理的。
我能做些什么来改变它,使它在GCC中工作,不涉及删除构造函数或在联合中命名结构?
使程序定义良好的c++的唯一方法是不使用匿名结构体。
奖励答案:如果您希望在写入normal
或d
或反之亦然之后读取elements
,那么这也是不允许的。程序的行为将是未定义的。
我如何使不同的命名属性与重叠的内存?除了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
等可以隐式转换为浮点型,elements
可以隐式转换为浮点型。