C ++成员变量别名?

时间:2009-01-30 05:50:23

标签: c++ struct variables alias member

我很确定这是可能的,因为我很确定我已经看过它了。我认为这很棒,但我很乐意接受“这是一个可怕的想法,因为____”的答案。

假设我们有一个基本结构。

struct vertex
{
    float x, y, z;
};

现在,我想在这些变量上实现别名。

vertex pos;
vertex col;
vertex arr;

pos.x = 0.0f; pos.y = 0.5f; pos.z = 1.0f;
col.r = 0.0f; col.g = 0.5f; col.b = 1.0f;
arr[0] = 0.0f; arr[1] = 0.5f; arr[2] = 1.0f;

理想情况下,第三种语法与数组无法区分。也就是说,如果我将arr作为参考参数发送给期望其将存储数据的浮点数组的函数(例如,许多OpenGL glGet函数),它将正常工作。

你怎么看?可能?可能但很蠢?

12 个答案:

答案 0 :(得分:22)

我要做的是制作访问者:

struct Vertex {
    float& r() { return values[0]; }
    float& g() { return values[1]; }
    float& b() { return values[2]; }

    float& x() { return values[0]; }
    float& y() { return values[1]; }
    float& z() { return values[2]; }

    float  operator [] (unsigned i) const { return this->values_[i]; }
    float& operator [] (unsigned i)       { return this->values_[i]; }
    operator float*() const { return this->values_; }

private:
    float[3] values_;
}

答案 1 :(得分:13)

使用联盟?

union vertex
{
    struct { float x, y, z; };
    struct { float r, g, b; };
    float arr[3];
};

我不推荐它 - 它会导致混乱。


正如Adrian在他的回答中所指出的那样,ISO C ++不支持这种与匿名结构成员的联合。它适用于GNU G ++(当你打开'-Wall -ansi -pedantic'时,抱怨不支持)。它让人想起预先标准化的C天(前K& R 1st Edn),当结构元素名称必须在所有结构中都是唯一的时候,你可以使用缩小的符号来获得结构中的偏移量,并且你可以使用其他结构类型的成员名称 - 一种无政府状态。当我开始使用C(很久以前,但后K& R1)时,这已经是历史用法了。

C11(ISO / IEC 9899:2011)支持使用匿名联合成员(对于这两种结构)显示的符号,但不支持早期版本的C标准。 ISO / IEC 14882:2011(C ++ 11)第9.5节规定了匿名联合,但GNU g++(4.9.1)不接受-pedantic所示的代码,标识为“{{1 }}”。

由于这个想法会导致混乱,我并不特别担心它不符合标准;我不会使用这个机制来完成这个任务(我会对在联合中使用匿名结构持谨慎态度,即使它是有益的。)


提出了一个问题:

  

三个(x-y-z,r-g-b和数组)不一定对齐。

这是一个有三个要素的联盟;这三个元素从同一地址开始。前两个是包含3个浮点值的结构。没有继承,并且没有虚拟函数可以提供不同的布局等。结构将以三个连续的元素布局(实际上,即使标准允许填充)。该数组也从相同的地址开始,并且在结构中经历“无填充”,元素与两个结构重叠。我真的没有看到会有问题。

答案 2 :(得分:13)

union中的无名嵌套结构不是标准C ++。但是,这应该有效:

struct Vertex
{
private:
   typedef float Vertex::* const vert[3];
   static const vert v;

public:
   typedef size_t size_type;
   float x, y, z;

   const float& operator[](size_type i) const {
      return this->*v[i];
   }

   float& operator[](size_type i) {
      return this->*v[i];
   }

};

const Vertex::vert Vertex::v = {&Vertex::x, &Vertex::y, &Vertex::z};

编辑:更多信息。该struct使用3个指针到数据成员的数组来访问重载[]运算符中的数据。

“typedef float Vertex :: * const vert”这一行意味着vert是指向Vertex结构的float成员的指针。 [3]意味着它是其中3个的数组。在重载的operator []中,索引此数组并取消引用指向数据的成员并返回值。

此外,无论打包问题如何,此方法都可以正常工作 - 编译器可以自由填充顶点结构,但它仍然可以正常工作。如果浮动包装不同,匿名联合将遇到问题。

答案 3 :(得分:3)

你可以通过其他人提到的工会来获得这个。将颜色和位置重载到这样的相同结构上可能不是一个好主意(例如,添加两种颜色通常意味着你想要饱和到1.0,而添加矢量则线性地发生),但是在它们之上覆盖一个float []就像这是非常好的,是一种广为接受的与GL / DirectX /等交换数据的方法。

我建议你避免在相同的函数范围内使用不同的别名引用相同的成员,因为这会将你带入一个令人讨厌的硬件停顿,叫做load-hit-store。特别是,如果可以,请避免这种情况:

vector foo; 
foo.x = 1.0f;
return foo[0] + foo[1];

答案 4 :(得分:3)

参考?

template<typename T>
struct vertex {
    vertex() :
        r(data[0]), g(data[1]), b(data[2]),
        x(data[0]), y(data[1]), z(data[2])
    {
    }

    T *operator *() {
        return data;
    }

    const T *operator *() const {
        return data;
    }

    T data[3];
    T &r, &g, &b;
    T &x, &y, &z;
};

答案 5 :(得分:2)

我猜你可以做一些宏观魔术来获得你想要的东西。 但那看起来很难看。为什么要为3种不同类型使用相同的struct,vertex?为什么不能为颜色定义类? 还要记住,顶点和颜色不一样。如果您将某些内容更改为顶点,那么如果您对两者都具有相同的类,那么这也会影响颜色。

答案 6 :(得分:2)

我不确定我是否正确理解了这个问题。但看起来你需要重载operator []以提供对struct / class的数组访问。请参阅此处提到的示例:Operator overloading

答案 7 :(得分:2)

以下结构将具有所请求的行为:

struct vertex
{
private:
    float data[3];
public:
    float &x, &y, &z;
    float &r, &g, &b;

    vertex() : x(data[0]), y(data[1]), z(data[2]), r(data[0]), g(data[1]), b(data[2]) {
    }

    float& operator [](int i) { 
        return data[i];
    }
};

答案 8 :(得分:1)

我认为不好的想法,至少对于给出的例子:缺点是,对于任何解决方案,你可能会自由地将“rgb”实例分配到/来自“xyz”实例,这可能很少是明智或正确的。即你冒着放弃一些有用的类型安全的风险。

就个人而言,就你给出的例子而言,我将从基类boost::array<float,3>或类似的子类化rgb和xyz类型。因此它们都继承了operator [],可以传递给期望数组的函数,并且通过更多类型的安全性传递给期望颜色/坐标的东西。通常你想要将xyz或rgb视为一个数组,但很少想要将xyz视为rgb,反之亦然。 (rgb IS-A数组:好的.xyz IS-A数组:好的.rgb IS-A xyz ????我不这么认为!)

当然,这意味着可以访问x,y,z&amp; r,g,b需要通过访问者(转发到适当的operator[](...))而不是直接发送给成员。 (你需要C#的属性)。

答案 9 :(得分:0)

下面有一个模板和两个Vector类,一个是疯狂的,一个是理智的。该模板实现了一个简单的固定编译时值数组。它专为子类化而设计,并使用受保护的数组变量来避免必须跳过箍来访问数组。 (有些人可能不喜欢这样的设计。我说,如果你的子类正在调用你的重载运算符,耦合可能是一个好主意。)

疯狂的类允许你有成员变量x,y,z,它就像一个数组,用于调用glGetFloatV。理智的人只有访问函数x(),y(),z(),并且仍然可以使用glGetFloatV。您可以使用任一类作为可能传递给OpenGL库的其他矢量对象的基础。虽然下面的类是特定于点的,但显然你只需要进行搜索/替换就可以将它们变成rgb颜色类。

疯狂的课很疯狂,因为语法糖vec.x而不是vec.x()的成本是3个参考变量。这可能会占用大量应用程序中的大量空间。使用更简单的理智版本。

template <typename T, int N>
class FixedVector {
protected:
    T arr[N];
public:
    FixedVector();

    FixedVector(const T* a) {
        for (int i = 0; i < N; ++i) {
            arr[i] = a[i];
        }
    }

    FixedVector(const T& other) {
        for (int i = 0; i < N; ++i) {
            arr[i] = other.arr[i];
        }
    }

    FixedVector& operator=(const T& other) {
        for (int i = 0; i < N; ++i) {
            arr[i] = other.arr[i];
        }
        return *this;
    }

    T* operator&() { return arr; }
    const T* operator&() const { return arr; }

    T& operator[](int ofs) { 
        assert(ofs >= 0 && ofs < N);
        return arr[ofs];
    }
    const T& operator[](int ofs) const { 
        assert(ofs >= 0 && ofs < N);
        return arr[ofs];
    }
};

class CrazyPoint :  public FixedVector<float, 3> {
public:
    float &x, &y, &z;

    CrazyPoint()
      : x(arr[0]), y(arr[1]), z(arr[2])
    { arr[0] = arr[1] = arr[2] = 0.0; }

    CrazyPoint(const float* a)
      : x(arr[0]), y(arr[1]), z(arr[2])
    {
        arr[0] = a[0];
        arr[1] = a[1];
        arr[2] = a[2];
    }

    CrazyPoint(float a, float b, float c) 
      : x(a), y(b), z(c)
    {
        arr[0] = a;
        arr[1] = b;
        arr[2] = c;
    }
};

class SanePoint : public FixedVector<float, 3> {
public:
    float& x() { return arr[0]; }
    float& y() { return arr[1]; }
    float& z() { return arr[2]; }

    SanePoint() { arr[0] = arr[1] = arr[2] = 0.0; }
    SanePoint(float a, float b, float c) 
    {
        arr[0] = a;
        arr[1] = b;
        arr[2] = c;
    }
};

// usage
SanePoint normal;
glGetFloatV(GL_CURRENT_NORMAL, &normal);

答案 10 :(得分:0)

您可以尝试添加对变量的引用,如下所示:

struct test {
        float x, y, z;
        float &r, &g, &b;

        test() : r(x), g(y), b(z) {}
    };

但是你的结构变得更大(从12个字节到40个字节)。

要在其上使用[],请使用operator []的重载,如前所述。

答案 11 :(得分:0)

关于使用指向值成员的引用成员的警告。如果您复制了这样的对象(比如按值传递),则需要定义复制构造函数(也可能还有赋值运算符)。默认的复制构造函数将为您提供一个副本,其引用成员指向原始对象的值成员,而不是新对象的值。这肯定不是你想要的。

考虑到你最终会得到更大的对象,正如已经指出的那样,我认为使用访问器方法比参考成员更受欢迎。