由于您的类型是标准布局,我认为根据C++标准,唯一合法的方法是使用包含具有自定义operator=定义的子对象的union。
使用union,您可以查看活动成员的公共初始序列,前提是所有类型都是标准布局类型。因此,如果我们精心制作一个对象,该对象共享相同的常规成员(例如按相同顺序排列的3个float对象),那么我们可以在不违反严格别名的情况下在它们之间“交换”。
为了实现这一点,我们需要创建一堆具有相同数据以标准布局类型相同顺序的成员。
作为一个简单的例子,让我们创建一个基本的代理类型:
template <int...Idx>
class Vector3Proxy
{
public:
template <int...UIdx,
typename = std::enable_if_t<(sizeof...(Idx)==sizeof...(UIdx))>>
auto operator=(const Vector3Proxy<UIdx...>& other) -> Vector3Proxy&
{
((m_data[Idx] = other.m_data[UIdx]),...);
return (*this);
}
auto operator=(float x) -> Vector3Proxy&
{
((m_data[Idx] = x),...);
return (*this);
}
private:
float m_data[3];
template <int...> friend class Vector3Proxy;
};
在这个例子中,不是所有的
m_data
成员都被使用了——但它们存在是为了满足“公共初始序列”的要求,这将允许我们通过其他标准布局类型在
union
中查看它。
可以根据需要构建更多内容;单组分运算符的
float
转换,支持算术等。
有了这样的类型,我们现在可以用这些代理类型构建
Vector3
对象。
struct Vector3
{
union {
float _storage[3];
Vector3Proxy<0> x;
Vector3Proxy<1> y;
Vector3Proxy<2> z;
Vector3Proxy<0,1> xy;
Vector3Proxy<1,2> yz;
Vector3Proxy<0,2> xz;
};
};
然后,这种类型可以轻松地用于一次性赋值给多个变量:
Vector3 x = {1,2,3};
x.xy = 5;
或将一个部分的组件分配给另一个部分:
Vector3 a = {1,2,3};
Vector3 b = {4,5,6};
a.xy = b.yz;
实时示例
此解决方案还确保sizeof(Vector3)
不会改变,因为所有代理对象的大小相同。
注意:在C++中使用带有匿名struct
的union
是无效的,尽管一些编译器支持它。因此,虽然重写如下可能很诱人:
union {
struct {
float x;
float y;
float z;
};
struct {
...
} xy;
}
这在标准C++中是无效的,也不是可移植的解决方案。
pos.set_yz(20)
?那能回答你的问题吗,还是你特别想要pos.yz = 20;
? - 463035818_is_not_a_numbersizeof(Vector3)
必须保持不变。 - Hrant Nurijanyan