Search code examples
c++templatesmathvectormath

Specialised constructor for Vector class


I'm trying to write a math vector class. A first version goes like :

  template <typename T, unsigned int n>
    struct Vector {
    Vector<T, n>(T t = T()) // default
    {
        for (int i = 0; i < n; i++)
        {
            data[i] = t;
        }
    }

    Vector<T, n>(const Vector<T, n> &aVector)
    {
        for (unsigned int i = 0; i < n; i++)
        {
            data[i] = aVector.data[i];
        }
    }

    Vector<T, n>(const T arr[n])
    {
        for (unsigned int i = 0; i < n; i++)
        {
            data[i] = arr[i];
        }
    }

    T& operator[](unsigned int i);
    const T& operator[](unsigned int i) const;
    Vector<T, n>& operator=(const Vector<T, n> &aVector);

    void normalise();   

    T data[n]; 
    };

I also have operators (+, *, etc.) declared outside the class, as well as a couple of other classes.

Here's the thing though. For vectors of length 2, 3, 4 I'd like to have a constructor (or a function) that takes x,y (for Vec2), x,y,z or x,y,z,w as parameters.

However, it seems that you can't make specialised constructors for this purpose. How should I proceed in that case ? Do I have to completely specialise three cases ? Wouldn't that imply that I have to rewrite chunks of code ?

I also have a similar Matrix class ( Matrix), and I'm pretty sure I'll need some constructors for rotation, translation, scaling, etc. I'm assuming I'll need to overcome a similar problem.

If you see

I also have operators (+, *, etc.) declared outside the class, as well as a couple of other functions (dot, cross, etc.).

Here's the thing though. For vectors of length 2, 3, 4 I'd like to have a constructor (or a function) that takes x,y (for Vec2), x,y,z or x,y,z,w as parameters.

However, it seems that you can't make specialised constructors for this purpose. How should I proceed in that case ? Do I have to completely specialise three cases ? Wouldn't that imply that I have to rewrite chunks of code ?

I also have a similar Matrix class ( Matrix), and I'm pretty sure I'll need some constructors for rotation, translation, scaling, etc. I'm assuming I'll need to overcome a similar problem.

If you see anything in the code that seems wrong to you, feel free to point it out by the way.

EDIT : In case I was not clear enough, the arrays are meant to be one-dimensional, and all of its components are of the same type. The specialisations are for arrays with 2, 3 and 4 elements.


Solution

  • You may utilize a variadic template:

    #include <chrono>
    #include <future>
    #include <iostream>
    #include <stdexcept>
    
    
    template<typename T, unsigned int n>
    struct Vector
    {
        // Note: We need x and y:
        // The default (in the question) is a constructor taking a single argument.
        template <typename ... Args>
        Vector(T x, T y, Args ... args)
        {
            static_assert(sizeof ... (Args) == n - 2, "Invalid number of arguments");
            auto initializer_list = { x, y, std::forward<Args>(args)... };
            std::copy(initializer_list.begin(), initializer_list.end(), data);
    
        }
    
        T data[n];
    };
    
    
    template<typename T, unsigned int n>
    void print(const Vector<T, n>& v) {
        for(unsigned i = 0; i < n; ++i)
            std::cout << v.data[i] << ' ';
        std::cout << '\n';
    }
    
    
    int main()
    {
        Vector<int, 2> v2(1, 2);
        Vector<int, 3> v3(1, 2, 3);
        Vector<int, 4> v4(1, 2, 3, 4);
        print(v2);
        print(v3);
        print(v4);
    
        // Invalid number of arguments
        // Vector<int, 3> e2(1, 2);
    
        // Invalid number of arguments
        // Vector<int, 3> e4(1, 2, 3, 4);
        return 0;
    }