I think the other answers sort of missed the point of the question:
I wish to access the members the same way as you would with this.
In other words, the question is really "how do I define a type vec
in a standard-compliant manner such that given an object u
of that type, u.x
, u.r
, and u.elements[0]
all refer to the same thing?"
Well, if you insist on that syntax...then the obvious answer is: references.
So:
template <typename some_type>
struct vec
{
vec() = default;
vec(const vec& other) : elements{ other.elements[0], other.elements[1], other.elements[2] } {}
vec & operator=(const vec &other) {
elements[0] = other.elements[0];
elements[1] = other.elements[1];
elements[2] = other.elements[2];
return *this;
}
some_type elements[3];
some_type &x = elements[0], &y = elements[1], &z = elements[2];
some_type &r = elements[0], &g = elements[1], &b = elements[2];
};
The first problem with this approach is that you need extra space for 6 reference members - which is rather expensive for such a small struct.
The second problem with this approach is that given const vec<double> v;
, v.x
is still of type double &
, so you could write v.x = 20;
and have it compile without warning or error - only to get undefined behavior. Pretty bad.
So, in the alternative, you might consider using accessor functions:
template <typename some_type>
struct vec
{
some_type elements[3];
some_type &x() { return elements[0]; }
const some_type &x() const { return elements[0]; }
some_type &y() { return elements[1]; }
const some_type &y() const { return elements[1]; }
some_type &z() { return elements[2]; }
const some_type &z() const { return elements[2]; }
some_type &r() { return elements[0]; }
const some_type &r() const { return elements[0]; }
some_type &g() { return elements[1]; }
const some_type &g() const { return elements[1]; }
some_type &b() { return elements[2]; }
const some_type &b() const { return elements[2]; }
};
You would have to write u.x()
etc. instead of u.x
, but the space savings is considerable, you can also rely on the compiler-generated special member functions, it's trivially copyable if some_type
is (which enables some optimizations), it's an aggregate and so can use the aggregate initialization syntax, and it's also const-correct.
Demo. Note that sizeof(vec<double>)
is 72 for the first version and only 24 for the second.