This question already has answers here:
Closed 5 years ago.
How would you go about doing this in standard C++11/14 ? Because if I'm not mistaken this isn't standard compliant code with the anonymous structs.
I wish to access the members the same way as you would with this.
template <typename some_type>
struct vec
{
union {
struct { some_type x, y, z; };
struct { some_type r, g, b; };
some_type elements[3];
};
};
Yes, neither C++11 nor C++14 allow anonymous structs. This answer contains some reasoning why this is the case. You need to name the structs, and they also cannot be defined within the anonymous union.
§9.5/5 [class.union]
...
The member-specification of an anonymous union shall only define non-static data members. [ Note: Nested types, anonymous unions, and functions cannot be declared within an anonymous union. —end note ]
So move the struct definitions outside of the union.
template <typename some_type>
struct vec
{
struct xyz { some_type x, y, z; };
struct rgb { some_type r, g, b; };
union {
xyz a;
rgb b;
some_type elements[3];
};
};
Now, we require some_type
to be standard-layout because that makes all the members of the anonymous union layout compatible. Here are the requirements for a standard layout type. These are described in section §9/7 of the standard.
Then, from §9.2 [class.mem]
16 Two standard-layout struct (Clause 9) types are layout-compatible if they have the same number of non-static data members and corresponding non-static data members (in declaration order) have layout-compatible types (3.9).
18 If a standard-layout union contains two or more standard-layout structs that share a common initial sequence, and if the standard-layout union object currently contains one of these standard-layout structs, it is permitted to inspect the common initial part of any of them. Two standard-layout structs share a common initial sequence if corresponding members have layout-compatible types and either neither member is a bit-field or both are bit-fields with the same width for a sequence of one or more initial members.
And for the array member, from §3.9/9 [basic.types]
...
Scalar types, standard-layout class types (Clause 9), arrays of such types
and cv-qualified versions of these types (3.9.3) are collectively called standard-layout types.
To ensure that some_type
is standard layout, add the following within the definition of vec
static_assert(std::is_standard_layout<some_type>::value, "not standard layout");
std::is_standard_layout
is defined in the type_traits
header. Now all 3 members of your union are standard layout, the two structs and the array are layout compatible, and so the 3 union members share a common initial sequence, which allows you to write and then inspect (read) any members belonging to the common initial sequence (the entire thing in your case).
Anonymous unions are allowed in C++11/14. See the example of their usage at Bjarne Stroustrup's C++11 FAQ
Regarding anonymous structs see Why does C++11 not support anonymous structs, while C11 does? and Why does C++ disallow anonymous structs and unions?
Though most compilers support anonymous structs, if you want your code to be standard compliant you have to write something like this:
template <typename some_type>
struct vec
{
union {
struct { some_type x, y, z; } s1;
struct { some_type r, g, b; } s2;
some_type elements[3];
};
};
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.