开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜