我有一个矢量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<3>
: -
class Vector3D : public VecFloatFix<3>{
//some 3D-specific function
};
除了有很多用户代码直接访问Vector3D::x,y,z
外,一切似乎都很好。
是否可以在不破坏用户代码的情况下从Vector3D
派生VecFloatFix<3>
?
我最好的猜测是: -
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
是不可持续的。
如果我有一个源自Vector2D
的新类VecFloatFix<2>
,Vector2D::z
将编译正常(危险)。
答案 0 :(得分:2)
这绝不可保证工作,因为它使用实现定义的和可能未定义的行为。 明智的实现可能表现得如预期的那样。
template<int constSize>
class VecFloatFix{
public:
union {
float database[constSize];
struct {
int x, y, z;
};
};
};
这也使database
公开。没有看到解决这个问题的方法,但是无论如何你提供operator[]
都没什么大不了的。
这假定为constSize >= 3
。如果你需要更小的尺寸,这可以通过更多的hackery来实现。所有向量都将包含x
y
和z
成员,但只有3D及以上才能使用它们。 2D向量只能使用x
和y
(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;
答案 1 :(得分:2)
此版本仅为大小为3的向量公开x
,y
,z
个组件。显然,其他大小也可能是专门的。
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]
的索引操作符。