使矢量 3D 源自矢量 ND,需要保留场 x y z

make vector 3D derived from vector ND, need to retain field x y z

本文关键字:保留 3D ND      更新时间:2023-10-16

我有一个矢量 3D 类

class Vector3D{
    public: float x; float y; float z;
    //some functions, e.g. operator+ - * / 
    //some 3D-specific function
};

和矢量 N-D 类。

template<int constSize> class VecFloatFix{
    float database[constSize];
    //some functions, e.g. operator+ - * / 
};

注意到两个类之间存在代码重复,所以我认为我应该从VecFloatFix<3>派生Vector3D:-

class Vector3D : public VecFloatFix<3>{
    //some 3D-specific function
};

一切似乎都很好,除了有很多用户代码直接访问Vector3D::x,y,z

是否可以在不破坏用户代码的情况下从VecFloatFix<3>派生Vector3D

我最好的猜测是:-

template<int constSize> class VecFloatFix{
    union{
        float database[constSize];
        float x,y,z;  ?????  sound like a hack
    }
    //some functions, e.g. operator+ - * / 
};

编辑:x,y,z硬编码到VecFloatFix是不可持续的。
如果我有一个派生自VecFloatFix<2>的新类Vector2DVector2D::z编译得很好(危险(。

这是一个只公开大小为 3 的向量的xyz分量的版本。显然,其他尺寸也可能是专门的。

template<int constSize> struct VecFloatStorage
{
    float database[constSize];
};
template<> struct VecFloatStorage<3>
{
    union
    {
        float database[3];
        struct { float x, y, z; };
    };
};
template<int constSize> class VecFloatFix : public VecFloatStorage<constSize>
{
public:
    // Methods go here.
};

我不知道该标准是否保证struct { float x, y, z; }具有与float data[3]相同的内存布局,但在实践中,我很确定这种假设成立。

GLM库正在使用类似的技巧,只是它们根本没有数组成员,而是提供了一个返回 (&this->x)[idx] 的索引运算符。

这绝不保证有效,因为它使用实现定义和可能未定义的行为。不过,明智的实现可能会按预期运行。

template<int constSize> 
class VecFloatFix{
public:
  union {
    float database[constSize];
    struct {
        int x, y, z;
    };
  };
};

这也database公开。看不到解决这个问题的方法,但没什么大不了的,因为无论如何你都提供了operator[]

这假设constSize >= 3 .如果您需要更小的尺寸,这可以通过更多的黑客来实现。所有向量都将具有x yz成员,但只有 3D 及以上版本才能将它们全部使用。2D 矢量将只有 xy可用(任何z的使用都可能导致错误(,而 1D 矢量将只有 x .注意 我拒绝对以下任何一项负责。

template<int constSize>
class VecFloatFix{
    public:
        union {
            float database[constSize];
            struct {
                float x;
            };
            struct {
                spacer<constSize, 1> sp1;
                typename spacer<constSize, 1>::type y;
            };
            struct {
                spacer<constSize, 2> sp2;
                typename spacer<constSize, 2>::type z;
            };
        };
};

其中spacer是这样定义的:

template <int N, int M, bool enable>
struct filler;
template <int N, int M>
struct filler<N, M, true>
{
    float _f[M];
    typedef float type;
};
template <int N, int M>
struct filler<N, M, false>
{
    struct nothing {};
    typedef nothing type;
};
template <int N, int M>
struct spacer
{
    filler<N, M, (N>M)> _f;
    typedef typename filler<N, M, (N>M)>::type type;
};

试驾:

VecFloatFix<4> vec4;
VecFloatFix<3> vec3;
VecFloatFix<2> vec2;
VecFloatFix<1> vec1;
`smoke test`
vec3.database[0] = 42;
vec2.database[1] = 99;
std::cout << vec3.x << std::endl;
std::cout << vec2.y << std::endl;
// make sure `y` aliases `database[1]`
std::cout << & vec2.y << std::endl;
std::cout << & vec2.database[1] << std::endl;
// make sure sizes are as expected
std::cout << sizeof(vec4) << " " << sizeof (vec3) << " " << sizeof(vec2) << " " << sizeof(vec1) << std::endl;