如何同时更新结构的多个字段?
假设我有一个结构
struct Vector3 {
float x;
float y;
float z;
};
请注意,sizeof(Vector3)
必须保持不变。
编辑:我对没有设置器的解决方案感兴趣。
我们不创建该结构实例Vector3 pos
。如何实现我的结构,以便我可以拥有类似pos.xy = 10 // updates x and y
、pos.yz = 20 // updates y and z
或pos.xz = 30 // updates x and z
的内容?
解决方案
由于您的类型是标准布局,我认为按照C++标准,执行此操作的唯一合法方法是使用包含具有自定义operator=
定义的子对象的union
。
使用union
,您可以查看活动成员的公共-初始序列,前提是所有类型都是标准布局类型。因此,如果我们仔细创建共享相同公共成员的对象(例如,3个float
对象按相同顺序),则可以在不违反严格别名的情况下在它们之间交换(&q;swizzle&q;)。
要实现这一点,我们需要创建一组成员,这些成员都具有相同顺序的标准布局类型的相同数据。
作为一个简单的示例,让我们创建一个基本代理类型:
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
的所有成员,但它们的存在是为了满足";common-初始序列";要求,这将允许我们通过union
中的其他标准布局类型查看它。
您可以根据需要构建此功能;float
转换单组件运算符,支持算术等。
有了这样的类型,我们现在可以从这些代理类型中构建Vector3
对象
struct Vector3
{
union {
float _storage[3]; // for easy initialization
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; // produces {5,6,3}
Live Example
此解决方案还确保sizeof(Vector3)
不会更改,因为所有代理对象的大小都相同。
注意:在C++中,将union
与匿名struct
一起使用是无效的,尽管有些编译器支持它。因此,尽管重写这段代码可能很诱人:
union {
struct {
float x;
float y;
float z;
}; // invalid, since this is anonymous
struct {
...
} xy;
}
这在标准C++中无效,并且不是可移植的解决方案。
相关文章