Robinson Robinson - 4 years ago 104
C++ Question

Anonymous struct with enable/disable

I have the following vector class (vector as in spatial, not array):

template<typename T0, size_t S, typename = typename std::enable_if<std::is_arithmetic<T0>::value && (S > 1 && S < 5)>::type>
struct Vec
{
using value_type = T0;
using vector_type = Vec<T0, S>;
using array_type = std::array<T0, S>;
using index_type = size_t;
using size_type = size_t;

enum { num_components = S };

array_type v;
};


, such that I can make a vector type with 2, 3 or 4 elements:

template<typename T0>
using Vec2 = Vec<T0, 2>;

template<typename T0>
using Vec3 = Vec<T0, 3>;

template<typename T0>
using Vec4 = Vec<T0, 4>;


Access is of the form v[0], v[1], etc. (for brevity I don't include the [] operator overloads). Sometimes I prefer x, y and so on but don't want the extra "." from naming the structs in the union. So using a non-standard "feature" of Visual Studio 2013, tried to use an anonymous union, only enabling the value if S (dimension) is 2, 3 or 4, as follows:

template<typename T0, size_t S, typename = typename std::enable_if<std::is_arithmetic<T0>::value && (S > 1 && S < 5)>::type>
struct Vec
{
using value_type = T0;
using vector_type = Vec<T0, S>;
using array_type = std::array<T0, S>;
using index_type = size_t;
using size_type = size_t;

enum { num_components = S };

union
{
array_type v;

template<typename = typename std::enable_if<S == 2>::type>
struct
{
value_type x, y;
};

template<typename = typename std::enable_if<S == 3>::type>
struct
{
value_type x, y, z;
};

template<typename = typename std::enable_if<S == 4>::type>
struct
{
value_type x, y, z, w;
};
};
};


Unfortunately this gives me the following error:

**error C2332: 'struct' : missing tag name**


And in a way I suppose it is. Is there any way to achieve what I'm trying here? I'm sure enable/disable of an anoymous struct almost certainly gives the compiler a migrane. I can use anonymous union like this if I give the struct a name of course.

Answer Source

Why do you decide that memory layout of std::array and struct{ T x, T y, ... T} will be identical? It can be reached only if you reset alignment setting to 1 for your class with #pragma pack. For others the alignment is unpredictable.

You want to have such class that

  1. provides access via data member selectors such as .x, .y and so on
  2. provides direct access to data member via operator[]
  3. does not break default data member alignment (std::array is linear, it breaks compiler optimization alignment for your class data member)

The following code meets the above requirements without any non-standard features:

template<typename T, size_t S>
struct Vec;
template<typename T>
struct Vec<T, 1> {
  enum {count = 1};
  T x;
  T& operator[](size_t i) {
    assert(i == 0);
    return x;
  }
  const T& operator[](size_t i) const {
    assert(i == 0);
    return x;
  }
};
template<typename T>
struct Vec<T, 2> {
  enum { count = 2 };
  T x;
  T y;
  T& operator[](size_t i) {
    assert(0 <= i && i < count);
    return this->*(pointer(i));
  }
  const T& operator[](size_t i) const {
    assert(0 <= i && i < count);
    return this->*(pointer(i));
  }
  static T Vec::* pointer(size_t i) {
    static T Vec::* a[count] = { &Vec::x, &Vec::y };
    return a[i];
  }
};
template<typename T>
struct Vec<T, 3> {
  enum { count = 3 };
  T x;
  T y;
  T z;
  T& operator[](size_t i) {
    assert(0 <= i && i < count);
    return this->*(pointer(i));
  }
  const T& operator[](size_t i) const {
    assert(0 <= i && i < count);
    return this->*(pointer(i));
  }
  static T Vec::* pointer(size_t i) {
    static T Vec::* a[count] = { &Vec::x, &Vec::y, &Vec::z };
    return a[i];
  }
};

int main() {
  Vec<int, 2> v1{ 1, 2 };
  assert(v1[0] == v1.x);
  assert(v1[1] == v1.y);

  Vec<unsigned char, 3> v2{ 4, 5, 6 };
  assert(v2[0] == v2.x);
  assert(v2[1] == v2.y);
  assert(v2[2] == v2.z);
  return 0;
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download