【问题标题】:make vector 3D derived from vector ND, need to retain field x y z从向量 AND 派生向量 3D,需要保留字段 x y z
【发布时间】:2017-03-19 09:57:58
【问题描述】:

我有一个矢量 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+ - * / 
};

我注意到两个类之间存在代码重复,所以我认为我应该让 Vector3D 派生自 VecFloatFix&lt;3&gt; :-

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

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

是否可以在不破坏用户代码的情况下使Vector3D 派生自VecFloatFix&lt;3&gt;

我最好的猜测是:-

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&lt;2&gt; 的新类Vector2DVector2D::z 将编译得很好(危险)。

【问题讨论】:

  • 如果VecFloatFix 中的databaseprotectedpublic,你可以在Vector3D 类中执行类似float&amp; x = database[0]; 的操作。
  • 有多种变通方法可能会得到您想要的,但这取决于您的客户端代码在做什么。最终,我会质疑您尝试做的事情的价值。只需将这两种类型分开,并为它们的互操作提供便利(例如,两者之间的转换)。
  • 在您的示例中,x,y,z 将被放置在一个浮点数(数据库 [0])中,因此 vec.x = 100.0 也会使 vec.y==100.0 。您可以使用struct {float x,y,z;}; 来避免这种情况。
  • AAlgirdasPreidžius 这很可能会导致每个对象浪费大量空间。
  • 引用通常占用空间(这是允许的,但标准没有强制要求)。

标签: c++ inheritance field unions


【解决方案1】:

这是一个版本,它只公开了大小为 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 库使用了类似的技巧,只是它们根本没有数组成员,而是提供了一个返回 (&amp;this-&gt;x)[idx] 的索引运算符。

【讨论】:

【解决方案2】:

由于它使用实现定义的和可能未定义的行为,因此无法保证它可以正常工作。 明智的实现将可能按预期运行。

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

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

假设constSize &gt;= 3。如果你需要更小的尺寸,这可以通过更多的黑客来实现。所有向量都将具有 x yz 成员,但只有 3D 及更高版本才能使它们全部可用。二维向量将只有xy 可用(任何z 的使用都可能导致错误),而一维向量将只有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;

【讨论】:

  • 这个黑客叫什么?
  • 不知道,但很讨厌。
  • 我知道您不会对任何损坏负责。 ...我只想告诉我可以访问vec2.z 而不会出现编译错误。 ideone.com/KAMNrm我猜这不是你的本意。
  • @Tulon 的版本似乎更简单、更干净,所以我可能应该删除我的。
  • 那是因为我有一个错字,typename spacer&lt;constSize, 1&gt;::type z; 应该是 2,而不是 1。固定。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-18
  • 1970-01-01
  • 2021-07-20
  • 1970-01-01
  • 2015-10-30
  • 1970-01-01
相关资源
最近更新 更多