简体   繁体   中英

Passing temporary struct as template argument

I'm in the process of creating a vector class and am trying to figure out ways to reuse the maximum amount of code for different size vectors. Here's a basic example:

template<typename T, unsigned int D>
class Vector
{
public:
    union {
        T v[D];
        struct {
            /* T x;
             * T y;
             * T z;
             * T w;
             */
        };
    };

    Vector()
    {
        for(unsigned int i=0; i<D; ++i)
            (*this)[i] = T(0);
    }
    Vector(T scalar)
    {
        for(unsigned int i=0; i<D; ++i)
            (*this)[i] = scalar;
    }

    inline T operator[](int i) { return (*this).v[i]; }
};

I want the member variables to be publicly accessible. Ex:

Vector<float,2> vec;
printf("X: %.2f, Y: %.2f\n", vec.x, vec.y);

What I'd like to do is something along the lines of this:

template<typename T>
class Vector2 : public Vector<T,2, struct { T x; T y; }> {};

template<typename T>
class Vector3 : public Vector<T,2, struct { T x; T y; T z; }> {};

and have it override a struct in the union:

template<typename T, unsigned int D, struct C>
class Vector
{
public:
    union {
        T v[D];
        // Place the passed struct here
    };
};

Is there any feasible way to do this? I do not want to use anything other than the standard library if possible. Thanks in advance.

EDIT: After reading upon all of the answers, I have understood that the way I am using unions is incorrect! Thank you to @MM for pointing this out. I have since chosen to go a different route, but I have selected the answer that best fit what I was looking for at the time. Once again, thank you for all of the welcomed responses below!

What you are trying to do is not allowed.
Anyway, you can do this:

template<typename T>
struct S { T x; T y; };

template<typename T>
class Vector2 : public Vector<T,2,S<T>> {};

Or this:

template<typename T>
class Vector2 : public Vector<T,2,S> {};

In the second case, Vector can be defined as:

template<typename T, unsigned int D, template<typename> class S>
class Vector {
    using MyStruct = S<T>;

    // ...

    union {
        T v[D];
        MyStruct myStruct;
    };
};

It doesn't scale very well to a large D , but if you're just after the four to six variants I'm imagining, you could partial-specialize a base class:

#include <iostream>

template<typename T, size_t D>
struct VectorBase;

template<typename T>
struct VectorBase<T, 2>
{
    constexpr VectorBase() : v{} {}
    union {
        T v[2];
        struct { T x, y; };
    };
};

template<typename T>
struct VectorBase<T, 3>
{
    constexpr VectorBase() : v{} {}
    union {
        T v[3];
        struct { T x, y, z; };
    };
};

template<typename T>
struct VectorBase<T, 4>
{
    constexpr VectorBase() : v{} {}
    union {
        T v[4];
        struct { T x, y, z, w; };
    };
};

template<typename T, size_t D>
struct Vector : public VectorBase<T, D>
{
    using VectorBase<T, D>::v;
    using size_type = decltype(D);
    using value_type = T;

    constexpr Vector() : VectorBase<T,D>{} {}
    constexpr Vector(T scalar) {
        std::fill(std::begin(v), std::end(v), scalar);
    }

    constexpr T& operator[](size_type i) const noexcept { return v[i]; }
    constexpr const T& operator[](size_type i) noexcept { return v[i]; }

    constexpr size_type size() const noexcept { return D; }

    constexpr T* data() noexcept { return &v[0]; }
    constexpr const T* data() const noexcept { return &v[0]; }
};

template<typename T>
using Vector2 = Vector<T, 2>;
template<typename T>
using Vector3 = Vector<T, 3>;
template<typename T>
using Vector4 = Vector<T, 4>;

int main() {
    Vector3<int> v{1};

    std::cout << v[0] << ", " << v.z << "\n";
    return 0;
}

Live demo: http://ideone.com/T3QHoq

If I understood you correctly your main purpose was to to declare order of fields that corresponds to the array elements of templated class. You cannot do this directly as templates does not accept inline type as parameter. To workaround the problem you could play with non-type template parameters to bind some labels to given index of an array:

#include <cstdio>
#include <unordered_map>
#include <utility>

struct Label { } x, y, z, w;

template <Label&... labels>
struct Pack { };

template <class, class>
struct VectorParent;

template <Label&... labels, size_t... Is>
struct VectorParent<Pack<labels...>, std::index_sequence<Is...>> {
   static std::unordered_map<Label *, size_t> label_map;
};

template <Label&... labels, size_t... Is>
std::unordered_map<Label *, size_t> VectorParent<Pack<labels...>, std::index_sequence<Is...>>::label_map = {{&labels, Is}...};

struct LabelNotFound { };

template <class T, size_t N, Label&... labels>
struct Vector:VectorParent<Pack<labels...>, std::make_index_sequence<sizeof...(labels)>> {
   static_assert(N == sizeof...(labels),
       "the cound of labels should corespond to the number of elements of the vector");
   using VectorParent<Pack<labels...>, std::make_index_sequence<sizeof...(labels)>>::label_map;
   T t[N];
   T &operator->*(Label& l) {
      auto it = label_map.find(&l);
      if (it == label_map.end())
         throw LabelNotFound{};
      return t[it->second];
   }
};

int main() {
    Vector<float,2,x,y> vec;
    vec->*x = 10.0f;
    printf("X: %.2f, Y: %.2f\n", vec->*x, vec->*y); // prints: X: 10.00, Y: 0.00
    //vec->*w = 10.1f; //would throw an exception LabelNotFound
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM