4

Before anyone says anything I know this is probably not recommended but I am still curious if there is a better way to do it or reasons not to beyond just it's a strange thing to do.

I started looking into this because I wanted to access elements of an array directly with semantically named members in the class while still being able to iterate over the array and not have to call/create some getter or setter methods.

I have a class definition that looks something like this.

class Vertex{
    public:
    Vertex(float x,float y,float z,float w);
    float v[4];
    float &x,&y,&Z,&w;
};

And a constructor that looks like this. My question is. Is there a better way of doing what I am doing in the constructor?

Vertex::Vertex(float vx,float vy,float vz,float vw):
    x(*const_cast<float*>( &this->v[0] )),
    y(*const_cast<float*>( &this->v[1] )), 
    z(*const_cast<float*>( &this->v[2] )),
    w(*const_cast<float*>( &this->v[3] ))
{
    v[0]=vx;
    v[1]=vy;
    v[2]=vz;
    v[3]=vw;
}

EDIT

I'm an idiot... you can just do it like Jonathan Wakely said.

x(v[0]) 

I guess I had some other problems before when I tried it. Oh well.

Robert F.
  • 63
  • 5

3 Answers3

7
Vertex::Vertex(float vx,float vy,float vz,float vw):
    v { vx, vy, vz, vw },
    x(v[0]),
    y(v[1]), 
    z(v[2]),
    w(v[3])
{
}

I'd avoid writing reference members here. The reason is that reference members prevent defaulted (compiler generated) copy/assignment special members.

class Vertex{
  public:
    Vertex(float x,float y,float z,float w)
        : v { x, y, z, w } { }

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

    float const &x() const { return v[0]; }
    float const &y() const { return v[1]; }
    float const &z() const { return v[2]; }
    float const &w() const { return v[3]; }
  private:
    float v[4];
};
sehe
  • 374,641
  • 47
  • 450
  • 633
4

You could go this way too:

class Vertex
{
public:
  float x;
  float y;
  float z;
  float w;

  Vertex(float x, float y, float z, float w);

  float&
  operator[](int i)
  { return *(&x + i); }

  float
  operator[](int i) const
  { return *(&x + i); }
};

Probably, this variant is better (compared to other alternatives) because it requires less code and gives you the additional ability to iterate over Vertex in array-style.

Alexander Shukaev
  • 16,674
  • 8
  • 70
  • 85
  • 1
    I like this as well but it still feels kind of scary to do that kind pointer math on members of a class. – Robert F. Mar 20 '13 at 00:00
2

Personally I like @sehe's answer best but I'll give you an alternative.

struct Vector4 {
    float x;
    float y;
    float z;
    float w;
};

union VectorUnion {
    Vector4 vector;
    float array[4];
};

Then you can just use the VectorUnion inside your Vertex class or on its own...

I am concerned about the fact that this is a C construct and that C++ struct is slightly different (it includes the vtable etc) but I think that it should work.

Again, I think @sehe's answer is better.

nonsensickle
  • 4,438
  • 2
  • 34
  • 61
  • 2
    It is UB to access one leg of the union when another was written to. Besides, you should be checking that struct layout actually matches the array layout in the light of padding and alignment issues, even if you were going to risk UB. (One thing is not a problem though: Vector4 is POD, so it _is_ precisely the C style struct, and has no vtable) – sehe Mar 19 '13 at 23:31
  • @sehe: § 9.5/1 "If a standard-layout union contains several standard-layout structs that share a common initial sequence (9.2), and if an object of this standard-layout union type contains one of the standard-layout structs, it is permitted to inspect the common initial sequence of any of standard-layout struct members;" – Mooing Duck Mar 20 '13 at 00:00
  • @MooingDuck I don't see any common initial sequence. In fact, I don't see a union containing several (standard-layout) structs to begin with. I wasn't mentioning the whole plethora of special cases. (Unless you can convince me that an array counts as a stdlayout struct, in which case I still fear the congruence would end at the first element) – sehe Mar 20 '13 at 00:24
  • looking again, § 3.9/10 heavily implies that primitives nor arrays are standard layout structs, though a struct containing _only_ an array _is_. I'd consider this a defect in the spec. But § 9.2/17 definitely says that multiple members cannot "correspond" with an array. :( Apparently layout-compatible never applies if members are nested differently. I wouldn't call this a defect, but I believe it should be extended. – Mooing Duck Mar 20 '13 at 01:02