“追溯联盟” - 可以做到吗?

时间:2011-09-07 17:43:05

标签: c++ templates unions

我有两个类:一个模板类,一个继承它的常规类:

template <int N> class Vector
{
    float data[N];
    //etc. (math, mostly)
};

class Vector3 : public Vector<3>
{
    //Vector3-specific stuff, like the cross product
};

现在,我想在子类中使用x / y / z成员变量(完整成员,而不仅仅是getter - 我希望能够设置它们)。但是为了确保所有(继承的)数学运算成功,x必须引用与data[0]ydata[1]等相同的内存。基本上,我想要一个联合,但我不能在基类中声明一个,因为我不知道那时向量中的浮点数。

那么 - 这可以吗?是否有某种预处理器/ typedef /模板魔法可以实现我正在寻找的东西?

PS:我正在使用带有-std = c ++ 0x的g ++ 4.6.0,如果有帮助的话。

编辑:虽然引用会提供我正在寻找的语法,但理想的解决方案不会让类更大(而且引用也很多!A Vector<3>是12个字节。带引用的Vector3为40!)。

6 个答案:

答案 0 :(得分:8)

怎么样:

class Vector3 : public Vector<3>
{
public:
  // initialize the references...
  Vector3() : x(data[0]), y(data[1]), z(data[2]){}
private:
  float& x;
  float& y;
  float& z;
};

当然,如果你想让他们占据相同的空间,那就是另一个故事......

使用一点模板魔法,您可以执行以下操作...

#include <iostream>

template <int N, typename UnionType = void*> struct Vector
{
    union
    {
      float data[N];
      UnionType field;
    };

    void set(int i, float f)
    {
      data[i] = f;
    }

    // in here, now work with data
    void print()
    {
      for(int i = 0; i < N; ++i)
        std::cout << i << ":" << data[i] << std::endl;
    }
};

// Define a structure of three floats
struct Float3
{
  float x;
  float y;
  float z;
};

struct Vector3 : public Vector<3, Float3>
{
};

int main(void)
{
  Vector<2> v1;
  v1.set(0, 0.1);
  v1.set(1, 0.2);
  v1.print();

  Vector3 v2;
  v2.field.x = 0.2;
  v2.field.y = 0.3;
  v2.field.z = 0.4;
  v2.print();

}
编辑:阅读完评论后,我意识到我之前发布的内容确实没有什么不同,所以稍微调整一下前一次迭代就可以直接访问该字段(这就是我猜你想要的) - 我猜下面这个和Rob的解决方案之间的区别在于你不需要所有的专业化来反复实现所有的逻辑......

答案 1 :(得分:7)

模板专业化怎么样?

template <int N> class Vector
{
  public:
  float data[N];
};

template <>
class Vector<1>
{
  public:
  union {
    float data[1];
    struct {
      float x;
    };
  };
};

template <>
class Vector<2>
{
  public:
  union {
    float data[2];
    struct {
      float x, y;
    };
  };
};


template <>
class Vector<3>
{
  public:
  union {
    float data[3];
    struct {
      float x, y, z;
    };
  };
};

class Vector3 : public Vector<3>
{
};

int main() {
  Vector3 v3;
  v3.x;
  v3.data[1];
};

<小时/> 编辑好的,这是一种不同的方法,但它引入了额外的标识符。

template <int N> class Data
{
  public:
  float data[N];
};

template <> class Data<3>
{
  public:
  union {
    float data[3];
    struct {
      float x, y, z;
    };
  };
};

template <int N> class Vector
{
  public:
  Data<N> data;
  float sum() { }
  float average() {}
  float mean() {}
};

class Vector3 : public Vector<3>
{
};

int main() {
  Vector3 v3;
  v3.data.x = 0; // Note the extra "data".
  v3.data.y = v3.data.data[0];
};

答案 2 :(得分:3)

这是一种可能性,从我对this question的答案中得出的结论:

class Vector3 : public Vector<3>
{
public:
    float &x, &y, &z;

    Vector3() : x(data[0]), y(data[1]), z(data[2]) { }
};

这有一些问题,比如要求你定义自己的复制构造函数,赋值运算符等。

答案 3 :(得分:1)

您可以进行以下操作:

template <int N> struct Vector
{
    float data[N];
    //etc. (math, mostly)
};
struct Vector3_n : Vector<3>
{
    //Vector3-specific stuff, like the cross product
};
struct Vector3_a
{
    float x, y, z;
};
union Vector3
{
    Vector3_n n;
    Vector3_a a;
};

现在:

Vector3 v;
v.n.CrossWhatEver();
std::cout << v.a.x << v.a.y << v.a.z

您可以尝试使用匿名联合技巧,但这不是标准的,也不是非常便携的。

但请注意,通过这种联合,即使没有注意到也很容易陷入未定义的行为。不过,它可能大部分都会起作用。

答案 4 :(得分:1)

我曾经写过一段时间(也允许吸气者/制定者),但这是一个非常便携的装扮黑客你真的不应该这样做。但是,无论如何,我以为我会扔掉它。基本上,它使用一个特殊类型,每个成员有0个数据。然后,该类型的成员函数获取this指针,计算父Vector3的位置,然后使用Vector3成员访问数据。这个hack或多或少像引用一样,但没有额外的内存,没有重新安装问题,我很确定这是未定义的行为,因此它可能导致nasal demons

class Vector3 : public Vector<3>
{
public: 
    struct xwrap {
        operator float() const;
        float& operator=(float b);
        float& operator=(const xwrap) {}
    }x;
    struct ywrap {
        operator float() const;
        float& operator=(float b);
        float& operator=(const ywrap) {}
    }y;
    struct zwrap {
        operator float() const;
        float& operator=(float b);
        float& operator=(const zwrap) {}
    }z;
    //Vector3-specific stuff, like the cross product 
};
#define parent(member) \
(*reinterpret_cast<Vector3*>(size_t(this)-offsetof(Vector3,member)))

Vector3::xwrap::operator float() const {
    return parent(x)[0];
}
float& Vector3::xwrap::operator=(float b) {
    return parent(x)[0] = b;
}
Vector3::ywrap::operator float() const {
    return parent(y)[1];
}
float& Vector3::ywrap::operator=(float b) {
    return parent(y)[1] = b;
}
Vector3::zwrap::operator float() const {
    return parent(z)[2];
}
float& Vector3::zwrap::operator=(float b) {
    return parent(z)[2] = b;
}

答案 5 :(得分:0)

要完成一个老问题:不。这让我感到难过,但你无法做到。

你可以近距离接触。比如:

Vector3.x() = 42;

Vector3.x(42);

Vector3.n.x = 42;

甚至

Vector3.x = 42; //At the expense of almost quadrupling the size of Vector3!

是触手可及的(见其他答案 - 他们都非常好)。但我的理想

Vector3.x = 42; //In only 12 bytes...

只是不可行。如果您想从基类继承所有函数,那就不是。

最后,有问题的代码最终得到了相当多的调整 - 它现在是严格的4成员向量(x,y,z,w),使用SSE进行矢量数学运算,并且有多个几何类(Point,矢量,比例等),因此继承核心功能不再是出于类型正确性原因的选择。它就是这样。

希望这可以节省其他人几天的沮丧搜索!