Named Structures in C++ Unions
In C++, I'm trying to create a specialized point class as a union, like so:
union point
{
struct { float x, y, z; };
float val[3];
float operator[](unsigned i) { return val[i]; }
};
So that I can access the point as an array or as multiple points, for readability.
However, let's say that I want to generalise this a bit:
template<unsigned n>
union point
{
struct { float ???; };
float val[n];
float operator[](unsigned i) { return val[i]; }
};
What can I put for ???
? I could have x
, x, y
, x, y, z
, or x, y, z, w
depending on what n
is. Solution? Forward declarations!
template<unsigned n>
union point
{
开发者_如何学Python struct coords;
float val[n];
float operator[](unsigned i) { return val[i]; }
};
template<>
struct point::coords<3>
{
float x, y, z;
};
// ...
But this doesn't appear to work. Under the GCC 4.6, it compiles, however, whenever that I try to use the members, like so:
point<3> val;
val.x;
I get the error:
error: ‘union point<3>’ has no member named ‘x’
Even if I change val.x
to val.coords::x
, I still get the error:
error: ‘union point<3>::coords’ is not a base of ‘union point<3>’
Adding using coords;
in the union definition didn't help, either.
Is there any way to accomplish this under the GCC 4.6? Is there a different method of doing this? Is it even possible?
I would suggest using variadic macro to define your union<N>
templates.
template<unsigned int N>
union point; // declared and undefined
#define DECLARE_POINT(NUM, ...) \
template<> \
union point<NUM> \
{ \
struct { float __VA_ARGS__; }; \
float val[NUM]; \
}
#undef DECLARE_POINT
Having done this, you can simply declare/define your various combinations for coordinates (before #undef
in this case):
DECLARE_POINT(1, x);
DECLARE_POINT(2, x, y);
DECLARE_POINT(3, x, y, z);
that is equivalent to,
template<> union point<1> { struct { float x; }; float val[1]; };
template<> union point<2> { struct { float x, y; }; float val[2]; };
template<> union point<3> { struct { float x, y, z; }; float val[3]; };
It can be used in the same way you asked:
point<3> p;
p.z = 0;
Also, you can put a cross check using some template trickery (static_assert
) to check the number arguments(e.g. 1,2,3,...
) match the total argument passed (e.g. x,y,z,...
).
This line inside your union:
struct coords;
forward-declares the type coords
, but there's no struct coords
field in your templated union.
Besides, only members of anonymous structures can be accessed as top-level fields of an union. For instance:
union foo {
struct { // anonymous struct
short i;
short j;
};
int k;
};
foo f;
// it's possible to access the fields of the anonymous struct as if they were
// direct members of the union
f.i = 4;
f.j = 8;
std::cout << f.k;
I'm not sure you'll be able to do that if you only specialize the inner struct
type.
However, this works:
template<unsigned n>
union point;
template<>
union point<2> {
struct { float x, y; };
float val[2];
};
template<>
union point<3> {
struct { float x, y, z; };
float val[3];
};
There is a number of downsides though; the main one being that you'll have to redefine operator[]
for each version of point
.
I know how to use templates but I'm no template god, so it's not out of question that a clever trick exists.
精彩评论