C++ math vector template

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
4
down vote

favorite












I wrote a basic template for a vector of arbitrary dimension to be used in a 3D-engine. Most operators make use of C++ template meta programming and instances are implicitly convertible to glm types. It is also possible to build a matrix class on top of this. Suggestions in terms of optimizations, safety, readability etc. are welcome.



Here we go:





#ifndef Log2VECTOR_H
#define Log2VECTOR_H

#include <glm/glm.hpp>
#include <ostream>

namespace Log2

template<unsigned Dim, typename T>
class Vector

public:
/**
* Constructors
*/
Vector() = default;
Vector(const T& value)

for (unsigned i = 0; i < Dim; i++)
_data[i] = value;


template<typename ...Args>
Vector(Args... args) : _data args...


/**
* Copy constructors
*/
Vector(const Vector& other) = default;
template<typename T1>
Vector(const Vector<Dim, T1>& other)

for (unsigned i = 0; i < Dim; i++)
_data[i] = static_cast<T>(other[i]);


Vector(const Vector<Dim - 1, T>& other, T scalar)

std::memcpy(_data, other.ptr(), sizeof other);
_data[Dim - 1] = scalar;

/**
* Concatenation
*/
template<unsigned Dim1>
Vector(const Vector<Dim1, T>& v1, const Vector<Dim - Dim1, T>& v2)

std::memcpy(_data, v1.ptr(), sizeof v1);
std::memcpy(_data + Dim1, v2.ptr(), sizeof v2);

/**
* Member access
*/
inline auto & operator (unsigned index)

return _data[index];

inline const auto & operator (unsigned index) const

return _data[index];

inline const auto * ptr() const

return _data;

template<unsigned D = Dim>
inline typename std::enable_if<D >= 4, Vector<3, T>>::type xyz() const

return Vector<3, T>(_data[0], _data[1], _data[2]);

template<unsigned D = Dim>
inline typename std::enable_if<D >= 3, Vector<2, T>>::type xz() const

return Vector<2, T>(_data[0], _data[2]);

template<unsigned D = Dim>
inline typename std::enable_if<D >= 3, Vector<2, T>>::type xy() const

return Vector<2, T>(_data[0], _data[1]);

inline const auto& x() const

return _data[0];

template<unsigned D = Dim>
inline typename std::enable_if<D >= 2, T>::type const & y() const

return _data[1];

template<unsigned D = Dim>
inline typename std::enable_if<D >= 3, T>::type const & z() const

return _data[2];

template<unsigned D = Dim>
inline typename std::enable_if<D >= 4, T>::type const & w() const

return _data[3];

inline const auto& r() const

return _data[0];

template<unsigned D = Dim>
inline typename std::enable_if<D >= 2, T>::type const & g() const

return _data[1];

template<unsigned D = Dim>
inline typename std::enable_if<D >= 3, T>::type const & b() const

return _data[2];

template<unsigned D = Dim>
inline typename std::enable_if<D >= 4, T>::type const & a() const

return _data[3];

template<unsigned index>
inline const auto& at() const

static_assert(index < Dim, "Invalid index");
return _data[index];

template<unsigned index>
inline auto& at()

static_assert(index < Dim, "Invalid index");
return _data[index];

/**
* Vector/vector calculations
*/
inline auto operator + (const Vector& other) const

Vector result;
ComputeAdd<Dim - 1>::call(*this, other, result);
return result;

inline auto operator - (const Vector& other) const

Vector result;
ComputeSub<Dim - 1>::call(*this, other, result);
return result;

inline auto operator * (const Vector& other) const

Vector result;
ComputeMul<Dim - 1>::call(*this, other, result);
return result;

inline auto operator / (const Vector& other) const

Vector result;
ComputeDiv<Dim - 1>::call(*this, other, result);
return result;

inline auto& operator += (const Vector& other)

ComputeAdd<Dim - 1>::call(*this, other, *this);
return *this;

inline auto& operator -= (const Vector& other)

ComputeSub<Dim - 1>::call(*this, other, *this);
return *this;

inline auto& operator *= (const Vector& other)

ComputeMul<Dim - 1>::call(*this, other, *this);
return *this;

inline auto& operator /= (const Vector& other)

ComputeDiv<Dim - 1>::call(*this, other, *this);
return *this;

/**
* Comparison operators
*/
inline auto operator == (const Vector& other) const

return ComputeEqual<Dim - 1>::call(*this, other);

inline auto operator != (const Vector& other) const

return ComputeInequal<Dim - 1>::call(*this, other);

inline auto operator < (const Vector& other) const

return ComputeLess<Dim - 1>::call(*this, other);

inline auto operator > (const Vector& other) const

return ComputeGreater<Dim - 1>::call(*this, other);

inline auto operator <= (const Vector& other) const

return ComputeLessOrEqual<Dim - 1>::call(*this, other);

inline auto operator >= (const Vector& other) const

return ComputeGreaterOrEqual<Dim - 1>::call(*this, other);

/**
* Vector length
*/
inline auto length() const

return std::sqrt(length2());

/**
* Squared Vector length
*/
inline auto length2() const

return dot(*this, *this);

/**
* Conversion from/to glm
*/
inline Vector(const glm::vec<Dim, T>& vec)

std::memcpy(_data, &vec[0], sizeof vec);

inline operator glm::vec<Dim, T>() const

glm::vec<Dim, T> vec;
std::memcpy(&vec[0], ptr(), sizeof vec);
return vec;

/**
* Change in dimension
*/
template<unsigned N>
inline operator Vector<N, T>() const

Vector<N, T> ret;
std::memcpy(&ret[0], ptr(), std::min(sizeof ret, sizeof *this));
return ret;

/**
* Debug output
*/
friend std::ostream& operator << (std::ostream& os, const Vector& vec)

os << "[";
for (unsigned i = 0; i < Dim; i++)
os << vec._data[i];
if (i != Dim - 1)
os << " ";


os << "]";
return os;

private:
T _data[Dim];

template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeAdd

static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)

result[index] = a[index] + b[index];
ComputeAdd<index - 1, D, Type>::call(a, b, result);

;
template<unsigned D, typename Type>
struct ComputeAdd<0, D, Type>

static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)

result[0] = a[0] + b[0];

;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeSub

static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)

result[index] = a[index] - b[index];
ComputeSub<index - 1, D, Type>::call(a, b, result);

;
template<unsigned D, typename Type>
struct ComputeSub<0, D, Type>

static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)

result[0] = a[0] - b[0];

;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeMul

static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)

result[index] = a[index] * b[index];
ComputeMul<index - 1, D, Type>::call(a, b, result);

;
template<unsigned D, typename Type>
struct ComputeMul<0, D, Type>

static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)

result[0] = a[0] * b[0];

;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeDiv

static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)

result[index] = a[index] / b[index];
ComputeDiv<index - 1, D, Type>::call(a, b, result);

;
template<unsigned D, typename Type>
struct ComputeDiv<0, D, Type>

static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)

result[0] = a[0] / b[0];

;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeEqual

static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

return a[index] == b[index] && ComputeEqual<index - 1, D, Type>::call(a, b);

;
template<unsigned D, typename Type>
struct ComputeEqual<0, D, Type>

static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

return a[0] == b[0];

;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeInequal

static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

;
template<unsigned D, typename Type>
struct ComputeInequal<0, D, Type>

static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

return a[0] != b[0];

;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeLess

static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

return a[index] < b[index] && ComputeLess<index - 1, D, Type>::call(a, b);

;
template<unsigned D, typename Type>
struct ComputeLess<0, D, Type>

static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

return a[0] < b[0];

;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeGreater

static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

return a[index] > b[index] && ComputeGreater<index - 1, D, Type>::call(a, b);

;
template<unsigned D, typename Type>
struct ComputeGreater<0, D, Type>

static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

return a[0] > b[0];

;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeLessOrEqual

static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

return a[index] <= b[index] && ComputeLessOrEqual<index - 1, D, Type>::call(a, b);

;
template<unsigned D, typename Type>
struct ComputeLessOrEqual<0, D, Type>

static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

return a[0] <= b[0];

;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeGreaterOrEqual

static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

return a[index] >= b[index] && ComputeGreaterOrEqual<index - 1, D, Type>::call(a, b);

;
template<unsigned D, typename Type>
struct ComputeGreaterOrEqual<0, D, Type>

static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

return a[0] >= b[0];

;
;

using Vec2f = Vector<2, float>;
using Vec3f = Vector<3, float>;
using Vec4f = Vector<4, float>;

using Vec2u = Vector<2, unsigned>;
using Vec3u = Vector<3, unsigned>;
using Vec4u = Vector<4, unsigned>;

using Vec2i = Vector<2, int>;
using Vec3i = Vector<3, int>;
using Vec4i = Vector<4, int>;


#endif


Edit:
Definition of the dot product for code completeness:





 template<unsigned Dim, typename T>
static inline T dot(const Vector<Dim, T>& a, const Vector<Dim, T>& b)

return ComputeDot<Dim, T, Dim - 1>::call(a, b);


template<unsigned Dim, typename T, unsigned index>
struct ComputeDot

static inline T call(const Vector<Dim, T>& a, const Vector<Dim, T>& b)

return a[index] * b[index] + ComputeDot<Dim, T, index - 1>::call(a, b);

;
template<unsigned Dim, typename T>
struct ComputeDot<Dim, T, 0>

static inline T call(const Vector<Dim, T>& a, const Vector<Dim, T>& b)

return a[0] * b[0];

;









share|improve this question



























    up vote
    4
    down vote

    favorite












    I wrote a basic template for a vector of arbitrary dimension to be used in a 3D-engine. Most operators make use of C++ template meta programming and instances are implicitly convertible to glm types. It is also possible to build a matrix class on top of this. Suggestions in terms of optimizations, safety, readability etc. are welcome.



    Here we go:





    #ifndef Log2VECTOR_H
    #define Log2VECTOR_H

    #include <glm/glm.hpp>
    #include <ostream>

    namespace Log2

    template<unsigned Dim, typename T>
    class Vector

    public:
    /**
    * Constructors
    */
    Vector() = default;
    Vector(const T& value)

    for (unsigned i = 0; i < Dim; i++)
    _data[i] = value;


    template<typename ...Args>
    Vector(Args... args) : _data args...


    /**
    * Copy constructors
    */
    Vector(const Vector& other) = default;
    template<typename T1>
    Vector(const Vector<Dim, T1>& other)

    for (unsigned i = 0; i < Dim; i++)
    _data[i] = static_cast<T>(other[i]);


    Vector(const Vector<Dim - 1, T>& other, T scalar)

    std::memcpy(_data, other.ptr(), sizeof other);
    _data[Dim - 1] = scalar;

    /**
    * Concatenation
    */
    template<unsigned Dim1>
    Vector(const Vector<Dim1, T>& v1, const Vector<Dim - Dim1, T>& v2)

    std::memcpy(_data, v1.ptr(), sizeof v1);
    std::memcpy(_data + Dim1, v2.ptr(), sizeof v2);

    /**
    * Member access
    */
    inline auto & operator (unsigned index)

    return _data[index];

    inline const auto & operator (unsigned index) const

    return _data[index];

    inline const auto * ptr() const

    return _data;

    template<unsigned D = Dim>
    inline typename std::enable_if<D >= 4, Vector<3, T>>::type xyz() const

    return Vector<3, T>(_data[0], _data[1], _data[2]);

    template<unsigned D = Dim>
    inline typename std::enable_if<D >= 3, Vector<2, T>>::type xz() const

    return Vector<2, T>(_data[0], _data[2]);

    template<unsigned D = Dim>
    inline typename std::enable_if<D >= 3, Vector<2, T>>::type xy() const

    return Vector<2, T>(_data[0], _data[1]);

    inline const auto& x() const

    return _data[0];

    template<unsigned D = Dim>
    inline typename std::enable_if<D >= 2, T>::type const & y() const

    return _data[1];

    template<unsigned D = Dim>
    inline typename std::enable_if<D >= 3, T>::type const & z() const

    return _data[2];

    template<unsigned D = Dim>
    inline typename std::enable_if<D >= 4, T>::type const & w() const

    return _data[3];

    inline const auto& r() const

    return _data[0];

    template<unsigned D = Dim>
    inline typename std::enable_if<D >= 2, T>::type const & g() const

    return _data[1];

    template<unsigned D = Dim>
    inline typename std::enable_if<D >= 3, T>::type const & b() const

    return _data[2];

    template<unsigned D = Dim>
    inline typename std::enable_if<D >= 4, T>::type const & a() const

    return _data[3];

    template<unsigned index>
    inline const auto& at() const

    static_assert(index < Dim, "Invalid index");
    return _data[index];

    template<unsigned index>
    inline auto& at()

    static_assert(index < Dim, "Invalid index");
    return _data[index];

    /**
    * Vector/vector calculations
    */
    inline auto operator + (const Vector& other) const

    Vector result;
    ComputeAdd<Dim - 1>::call(*this, other, result);
    return result;

    inline auto operator - (const Vector& other) const

    Vector result;
    ComputeSub<Dim - 1>::call(*this, other, result);
    return result;

    inline auto operator * (const Vector& other) const

    Vector result;
    ComputeMul<Dim - 1>::call(*this, other, result);
    return result;

    inline auto operator / (const Vector& other) const

    Vector result;
    ComputeDiv<Dim - 1>::call(*this, other, result);
    return result;

    inline auto& operator += (const Vector& other)

    ComputeAdd<Dim - 1>::call(*this, other, *this);
    return *this;

    inline auto& operator -= (const Vector& other)

    ComputeSub<Dim - 1>::call(*this, other, *this);
    return *this;

    inline auto& operator *= (const Vector& other)

    ComputeMul<Dim - 1>::call(*this, other, *this);
    return *this;

    inline auto& operator /= (const Vector& other)

    ComputeDiv<Dim - 1>::call(*this, other, *this);
    return *this;

    /**
    * Comparison operators
    */
    inline auto operator == (const Vector& other) const

    return ComputeEqual<Dim - 1>::call(*this, other);

    inline auto operator != (const Vector& other) const

    return ComputeInequal<Dim - 1>::call(*this, other);

    inline auto operator < (const Vector& other) const

    return ComputeLess<Dim - 1>::call(*this, other);

    inline auto operator > (const Vector& other) const

    return ComputeGreater<Dim - 1>::call(*this, other);

    inline auto operator <= (const Vector& other) const

    return ComputeLessOrEqual<Dim - 1>::call(*this, other);

    inline auto operator >= (const Vector& other) const

    return ComputeGreaterOrEqual<Dim - 1>::call(*this, other);

    /**
    * Vector length
    */
    inline auto length() const

    return std::sqrt(length2());

    /**
    * Squared Vector length
    */
    inline auto length2() const

    return dot(*this, *this);

    /**
    * Conversion from/to glm
    */
    inline Vector(const glm::vec<Dim, T>& vec)

    std::memcpy(_data, &vec[0], sizeof vec);

    inline operator glm::vec<Dim, T>() const

    glm::vec<Dim, T> vec;
    std::memcpy(&vec[0], ptr(), sizeof vec);
    return vec;

    /**
    * Change in dimension
    */
    template<unsigned N>
    inline operator Vector<N, T>() const

    Vector<N, T> ret;
    std::memcpy(&ret[0], ptr(), std::min(sizeof ret, sizeof *this));
    return ret;

    /**
    * Debug output
    */
    friend std::ostream& operator << (std::ostream& os, const Vector& vec)

    os << "[";
    for (unsigned i = 0; i < Dim; i++)
    os << vec._data[i];
    if (i != Dim - 1)
    os << " ";


    os << "]";
    return os;

    private:
    T _data[Dim];

    template<unsigned index, unsigned D = Dim, typename Type = T>
    struct ComputeAdd

    static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)

    result[index] = a[index] + b[index];
    ComputeAdd<index - 1, D, Type>::call(a, b, result);

    ;
    template<unsigned D, typename Type>
    struct ComputeAdd<0, D, Type>

    static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)

    result[0] = a[0] + b[0];

    ;
    template<unsigned index, unsigned D = Dim, typename Type = T>
    struct ComputeSub

    static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)

    result[index] = a[index] - b[index];
    ComputeSub<index - 1, D, Type>::call(a, b, result);

    ;
    template<unsigned D, typename Type>
    struct ComputeSub<0, D, Type>

    static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)

    result[0] = a[0] - b[0];

    ;
    template<unsigned index, unsigned D = Dim, typename Type = T>
    struct ComputeMul

    static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)

    result[index] = a[index] * b[index];
    ComputeMul<index - 1, D, Type>::call(a, b, result);

    ;
    template<unsigned D, typename Type>
    struct ComputeMul<0, D, Type>

    static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)

    result[0] = a[0] * b[0];

    ;
    template<unsigned index, unsigned D = Dim, typename Type = T>
    struct ComputeDiv

    static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)

    result[index] = a[index] / b[index];
    ComputeDiv<index - 1, D, Type>::call(a, b, result);

    ;
    template<unsigned D, typename Type>
    struct ComputeDiv<0, D, Type>

    static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)

    result[0] = a[0] / b[0];

    ;
    template<unsigned index, unsigned D = Dim, typename Type = T>
    struct ComputeEqual

    static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

    return a[index] == b[index] && ComputeEqual<index - 1, D, Type>::call(a, b);

    ;
    template<unsigned D, typename Type>
    struct ComputeEqual<0, D, Type>

    static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

    return a[0] == b[0];

    ;
    template<unsigned index, unsigned D = Dim, typename Type = T>
    struct ComputeInequal

    static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

    ;
    template<unsigned D, typename Type>
    struct ComputeInequal<0, D, Type>

    static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

    return a[0] != b[0];

    ;
    template<unsigned index, unsigned D = Dim, typename Type = T>
    struct ComputeLess

    static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

    return a[index] < b[index] && ComputeLess<index - 1, D, Type>::call(a, b);

    ;
    template<unsigned D, typename Type>
    struct ComputeLess<0, D, Type>

    static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

    return a[0] < b[0];

    ;
    template<unsigned index, unsigned D = Dim, typename Type = T>
    struct ComputeGreater

    static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

    return a[index] > b[index] && ComputeGreater<index - 1, D, Type>::call(a, b);

    ;
    template<unsigned D, typename Type>
    struct ComputeGreater<0, D, Type>

    static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

    return a[0] > b[0];

    ;
    template<unsigned index, unsigned D = Dim, typename Type = T>
    struct ComputeLessOrEqual

    static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

    return a[index] <= b[index] && ComputeLessOrEqual<index - 1, D, Type>::call(a, b);

    ;
    template<unsigned D, typename Type>
    struct ComputeLessOrEqual<0, D, Type>

    static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

    return a[0] <= b[0];

    ;
    template<unsigned index, unsigned D = Dim, typename Type = T>
    struct ComputeGreaterOrEqual

    static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

    return a[index] >= b[index] && ComputeGreaterOrEqual<index - 1, D, Type>::call(a, b);

    ;
    template<unsigned D, typename Type>
    struct ComputeGreaterOrEqual<0, D, Type>

    static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

    return a[0] >= b[0];

    ;
    ;

    using Vec2f = Vector<2, float>;
    using Vec3f = Vector<3, float>;
    using Vec4f = Vector<4, float>;

    using Vec2u = Vector<2, unsigned>;
    using Vec3u = Vector<3, unsigned>;
    using Vec4u = Vector<4, unsigned>;

    using Vec2i = Vector<2, int>;
    using Vec3i = Vector<3, int>;
    using Vec4i = Vector<4, int>;


    #endif


    Edit:
    Definition of the dot product for code completeness:





     template<unsigned Dim, typename T>
    static inline T dot(const Vector<Dim, T>& a, const Vector<Dim, T>& b)

    return ComputeDot<Dim, T, Dim - 1>::call(a, b);


    template<unsigned Dim, typename T, unsigned index>
    struct ComputeDot

    static inline T call(const Vector<Dim, T>& a, const Vector<Dim, T>& b)

    return a[index] * b[index] + ComputeDot<Dim, T, index - 1>::call(a, b);

    ;
    template<unsigned Dim, typename T>
    struct ComputeDot<Dim, T, 0>

    static inline T call(const Vector<Dim, T>& a, const Vector<Dim, T>& b)

    return a[0] * b[0];

    ;









    share|improve this question

























      up vote
      4
      down vote

      favorite









      up vote
      4
      down vote

      favorite











      I wrote a basic template for a vector of arbitrary dimension to be used in a 3D-engine. Most operators make use of C++ template meta programming and instances are implicitly convertible to glm types. It is also possible to build a matrix class on top of this. Suggestions in terms of optimizations, safety, readability etc. are welcome.



      Here we go:





      #ifndef Log2VECTOR_H
      #define Log2VECTOR_H

      #include <glm/glm.hpp>
      #include <ostream>

      namespace Log2

      template<unsigned Dim, typename T>
      class Vector

      public:
      /**
      * Constructors
      */
      Vector() = default;
      Vector(const T& value)

      for (unsigned i = 0; i < Dim; i++)
      _data[i] = value;


      template<typename ...Args>
      Vector(Args... args) : _data args...


      /**
      * Copy constructors
      */
      Vector(const Vector& other) = default;
      template<typename T1>
      Vector(const Vector<Dim, T1>& other)

      for (unsigned i = 0; i < Dim; i++)
      _data[i] = static_cast<T>(other[i]);


      Vector(const Vector<Dim - 1, T>& other, T scalar)

      std::memcpy(_data, other.ptr(), sizeof other);
      _data[Dim - 1] = scalar;

      /**
      * Concatenation
      */
      template<unsigned Dim1>
      Vector(const Vector<Dim1, T>& v1, const Vector<Dim - Dim1, T>& v2)

      std::memcpy(_data, v1.ptr(), sizeof v1);
      std::memcpy(_data + Dim1, v2.ptr(), sizeof v2);

      /**
      * Member access
      */
      inline auto & operator (unsigned index)

      return _data[index];

      inline const auto & operator (unsigned index) const

      return _data[index];

      inline const auto * ptr() const

      return _data;

      template<unsigned D = Dim>
      inline typename std::enable_if<D >= 4, Vector<3, T>>::type xyz() const

      return Vector<3, T>(_data[0], _data[1], _data[2]);

      template<unsigned D = Dim>
      inline typename std::enable_if<D >= 3, Vector<2, T>>::type xz() const

      return Vector<2, T>(_data[0], _data[2]);

      template<unsigned D = Dim>
      inline typename std::enable_if<D >= 3, Vector<2, T>>::type xy() const

      return Vector<2, T>(_data[0], _data[1]);

      inline const auto& x() const

      return _data[0];

      template<unsigned D = Dim>
      inline typename std::enable_if<D >= 2, T>::type const & y() const

      return _data[1];

      template<unsigned D = Dim>
      inline typename std::enable_if<D >= 3, T>::type const & z() const

      return _data[2];

      template<unsigned D = Dim>
      inline typename std::enable_if<D >= 4, T>::type const & w() const

      return _data[3];

      inline const auto& r() const

      return _data[0];

      template<unsigned D = Dim>
      inline typename std::enable_if<D >= 2, T>::type const & g() const

      return _data[1];

      template<unsigned D = Dim>
      inline typename std::enable_if<D >= 3, T>::type const & b() const

      return _data[2];

      template<unsigned D = Dim>
      inline typename std::enable_if<D >= 4, T>::type const & a() const

      return _data[3];

      template<unsigned index>
      inline const auto& at() const

      static_assert(index < Dim, "Invalid index");
      return _data[index];

      template<unsigned index>
      inline auto& at()

      static_assert(index < Dim, "Invalid index");
      return _data[index];

      /**
      * Vector/vector calculations
      */
      inline auto operator + (const Vector& other) const

      Vector result;
      ComputeAdd<Dim - 1>::call(*this, other, result);
      return result;

      inline auto operator - (const Vector& other) const

      Vector result;
      ComputeSub<Dim - 1>::call(*this, other, result);
      return result;

      inline auto operator * (const Vector& other) const

      Vector result;
      ComputeMul<Dim - 1>::call(*this, other, result);
      return result;

      inline auto operator / (const Vector& other) const

      Vector result;
      ComputeDiv<Dim - 1>::call(*this, other, result);
      return result;

      inline auto& operator += (const Vector& other)

      ComputeAdd<Dim - 1>::call(*this, other, *this);
      return *this;

      inline auto& operator -= (const Vector& other)

      ComputeSub<Dim - 1>::call(*this, other, *this);
      return *this;

      inline auto& operator *= (const Vector& other)

      ComputeMul<Dim - 1>::call(*this, other, *this);
      return *this;

      inline auto& operator /= (const Vector& other)

      ComputeDiv<Dim - 1>::call(*this, other, *this);
      return *this;

      /**
      * Comparison operators
      */
      inline auto operator == (const Vector& other) const

      return ComputeEqual<Dim - 1>::call(*this, other);

      inline auto operator != (const Vector& other) const

      return ComputeInequal<Dim - 1>::call(*this, other);

      inline auto operator < (const Vector& other) const

      return ComputeLess<Dim - 1>::call(*this, other);

      inline auto operator > (const Vector& other) const

      return ComputeGreater<Dim - 1>::call(*this, other);

      inline auto operator <= (const Vector& other) const

      return ComputeLessOrEqual<Dim - 1>::call(*this, other);

      inline auto operator >= (const Vector& other) const

      return ComputeGreaterOrEqual<Dim - 1>::call(*this, other);

      /**
      * Vector length
      */
      inline auto length() const

      return std::sqrt(length2());

      /**
      * Squared Vector length
      */
      inline auto length2() const

      return dot(*this, *this);

      /**
      * Conversion from/to glm
      */
      inline Vector(const glm::vec<Dim, T>& vec)

      std::memcpy(_data, &vec[0], sizeof vec);

      inline operator glm::vec<Dim, T>() const

      glm::vec<Dim, T> vec;
      std::memcpy(&vec[0], ptr(), sizeof vec);
      return vec;

      /**
      * Change in dimension
      */
      template<unsigned N>
      inline operator Vector<N, T>() const

      Vector<N, T> ret;
      std::memcpy(&ret[0], ptr(), std::min(sizeof ret, sizeof *this));
      return ret;

      /**
      * Debug output
      */
      friend std::ostream& operator << (std::ostream& os, const Vector& vec)

      os << "[";
      for (unsigned i = 0; i < Dim; i++)
      os << vec._data[i];
      if (i != Dim - 1)
      os << " ";


      os << "]";
      return os;

      private:
      T _data[Dim];

      template<unsigned index, unsigned D = Dim, typename Type = T>
      struct ComputeAdd

      static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)

      result[index] = a[index] + b[index];
      ComputeAdd<index - 1, D, Type>::call(a, b, result);

      ;
      template<unsigned D, typename Type>
      struct ComputeAdd<0, D, Type>

      static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)

      result[0] = a[0] + b[0];

      ;
      template<unsigned index, unsigned D = Dim, typename Type = T>
      struct ComputeSub

      static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)

      result[index] = a[index] - b[index];
      ComputeSub<index - 1, D, Type>::call(a, b, result);

      ;
      template<unsigned D, typename Type>
      struct ComputeSub<0, D, Type>

      static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)

      result[0] = a[0] - b[0];

      ;
      template<unsigned index, unsigned D = Dim, typename Type = T>
      struct ComputeMul

      static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)

      result[index] = a[index] * b[index];
      ComputeMul<index - 1, D, Type>::call(a, b, result);

      ;
      template<unsigned D, typename Type>
      struct ComputeMul<0, D, Type>

      static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)

      result[0] = a[0] * b[0];

      ;
      template<unsigned index, unsigned D = Dim, typename Type = T>
      struct ComputeDiv

      static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)

      result[index] = a[index] / b[index];
      ComputeDiv<index - 1, D, Type>::call(a, b, result);

      ;
      template<unsigned D, typename Type>
      struct ComputeDiv<0, D, Type>

      static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)

      result[0] = a[0] / b[0];

      ;
      template<unsigned index, unsigned D = Dim, typename Type = T>
      struct ComputeEqual

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      return a[index] == b[index] && ComputeEqual<index - 1, D, Type>::call(a, b);

      ;
      template<unsigned D, typename Type>
      struct ComputeEqual<0, D, Type>

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      return a[0] == b[0];

      ;
      template<unsigned index, unsigned D = Dim, typename Type = T>
      struct ComputeInequal

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      ;
      template<unsigned D, typename Type>
      struct ComputeInequal<0, D, Type>

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      return a[0] != b[0];

      ;
      template<unsigned index, unsigned D = Dim, typename Type = T>
      struct ComputeLess

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      return a[index] < b[index] && ComputeLess<index - 1, D, Type>::call(a, b);

      ;
      template<unsigned D, typename Type>
      struct ComputeLess<0, D, Type>

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      return a[0] < b[0];

      ;
      template<unsigned index, unsigned D = Dim, typename Type = T>
      struct ComputeGreater

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      return a[index] > b[index] && ComputeGreater<index - 1, D, Type>::call(a, b);

      ;
      template<unsigned D, typename Type>
      struct ComputeGreater<0, D, Type>

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      return a[0] > b[0];

      ;
      template<unsigned index, unsigned D = Dim, typename Type = T>
      struct ComputeLessOrEqual

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      return a[index] <= b[index] && ComputeLessOrEqual<index - 1, D, Type>::call(a, b);

      ;
      template<unsigned D, typename Type>
      struct ComputeLessOrEqual<0, D, Type>

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      return a[0] <= b[0];

      ;
      template<unsigned index, unsigned D = Dim, typename Type = T>
      struct ComputeGreaterOrEqual

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      return a[index] >= b[index] && ComputeGreaterOrEqual<index - 1, D, Type>::call(a, b);

      ;
      template<unsigned D, typename Type>
      struct ComputeGreaterOrEqual<0, D, Type>

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      return a[0] >= b[0];

      ;
      ;

      using Vec2f = Vector<2, float>;
      using Vec3f = Vector<3, float>;
      using Vec4f = Vector<4, float>;

      using Vec2u = Vector<2, unsigned>;
      using Vec3u = Vector<3, unsigned>;
      using Vec4u = Vector<4, unsigned>;

      using Vec2i = Vector<2, int>;
      using Vec3i = Vector<3, int>;
      using Vec4i = Vector<4, int>;


      #endif


      Edit:
      Definition of the dot product for code completeness:





       template<unsigned Dim, typename T>
      static inline T dot(const Vector<Dim, T>& a, const Vector<Dim, T>& b)

      return ComputeDot<Dim, T, Dim - 1>::call(a, b);


      template<unsigned Dim, typename T, unsigned index>
      struct ComputeDot

      static inline T call(const Vector<Dim, T>& a, const Vector<Dim, T>& b)

      return a[index] * b[index] + ComputeDot<Dim, T, index - 1>::call(a, b);

      ;
      template<unsigned Dim, typename T>
      struct ComputeDot<Dim, T, 0>

      static inline T call(const Vector<Dim, T>& a, const Vector<Dim, T>& b)

      return a[0] * b[0];

      ;









      share|improve this question















      I wrote a basic template for a vector of arbitrary dimension to be used in a 3D-engine. Most operators make use of C++ template meta programming and instances are implicitly convertible to glm types. It is also possible to build a matrix class on top of this. Suggestions in terms of optimizations, safety, readability etc. are welcome.



      Here we go:





      #ifndef Log2VECTOR_H
      #define Log2VECTOR_H

      #include <glm/glm.hpp>
      #include <ostream>

      namespace Log2

      template<unsigned Dim, typename T>
      class Vector

      public:
      /**
      * Constructors
      */
      Vector() = default;
      Vector(const T& value)

      for (unsigned i = 0; i < Dim; i++)
      _data[i] = value;


      template<typename ...Args>
      Vector(Args... args) : _data args...


      /**
      * Copy constructors
      */
      Vector(const Vector& other) = default;
      template<typename T1>
      Vector(const Vector<Dim, T1>& other)

      for (unsigned i = 0; i < Dim; i++)
      _data[i] = static_cast<T>(other[i]);


      Vector(const Vector<Dim - 1, T>& other, T scalar)

      std::memcpy(_data, other.ptr(), sizeof other);
      _data[Dim - 1] = scalar;

      /**
      * Concatenation
      */
      template<unsigned Dim1>
      Vector(const Vector<Dim1, T>& v1, const Vector<Dim - Dim1, T>& v2)

      std::memcpy(_data, v1.ptr(), sizeof v1);
      std::memcpy(_data + Dim1, v2.ptr(), sizeof v2);

      /**
      * Member access
      */
      inline auto & operator (unsigned index)

      return _data[index];

      inline const auto & operator (unsigned index) const

      return _data[index];

      inline const auto * ptr() const

      return _data;

      template<unsigned D = Dim>
      inline typename std::enable_if<D >= 4, Vector<3, T>>::type xyz() const

      return Vector<3, T>(_data[0], _data[1], _data[2]);

      template<unsigned D = Dim>
      inline typename std::enable_if<D >= 3, Vector<2, T>>::type xz() const

      return Vector<2, T>(_data[0], _data[2]);

      template<unsigned D = Dim>
      inline typename std::enable_if<D >= 3, Vector<2, T>>::type xy() const

      return Vector<2, T>(_data[0], _data[1]);

      inline const auto& x() const

      return _data[0];

      template<unsigned D = Dim>
      inline typename std::enable_if<D >= 2, T>::type const & y() const

      return _data[1];

      template<unsigned D = Dim>
      inline typename std::enable_if<D >= 3, T>::type const & z() const

      return _data[2];

      template<unsigned D = Dim>
      inline typename std::enable_if<D >= 4, T>::type const & w() const

      return _data[3];

      inline const auto& r() const

      return _data[0];

      template<unsigned D = Dim>
      inline typename std::enable_if<D >= 2, T>::type const & g() const

      return _data[1];

      template<unsigned D = Dim>
      inline typename std::enable_if<D >= 3, T>::type const & b() const

      return _data[2];

      template<unsigned D = Dim>
      inline typename std::enable_if<D >= 4, T>::type const & a() const

      return _data[3];

      template<unsigned index>
      inline const auto& at() const

      static_assert(index < Dim, "Invalid index");
      return _data[index];

      template<unsigned index>
      inline auto& at()

      static_assert(index < Dim, "Invalid index");
      return _data[index];

      /**
      * Vector/vector calculations
      */
      inline auto operator + (const Vector& other) const

      Vector result;
      ComputeAdd<Dim - 1>::call(*this, other, result);
      return result;

      inline auto operator - (const Vector& other) const

      Vector result;
      ComputeSub<Dim - 1>::call(*this, other, result);
      return result;

      inline auto operator * (const Vector& other) const

      Vector result;
      ComputeMul<Dim - 1>::call(*this, other, result);
      return result;

      inline auto operator / (const Vector& other) const

      Vector result;
      ComputeDiv<Dim - 1>::call(*this, other, result);
      return result;

      inline auto& operator += (const Vector& other)

      ComputeAdd<Dim - 1>::call(*this, other, *this);
      return *this;

      inline auto& operator -= (const Vector& other)

      ComputeSub<Dim - 1>::call(*this, other, *this);
      return *this;

      inline auto& operator *= (const Vector& other)

      ComputeMul<Dim - 1>::call(*this, other, *this);
      return *this;

      inline auto& operator /= (const Vector& other)

      ComputeDiv<Dim - 1>::call(*this, other, *this);
      return *this;

      /**
      * Comparison operators
      */
      inline auto operator == (const Vector& other) const

      return ComputeEqual<Dim - 1>::call(*this, other);

      inline auto operator != (const Vector& other) const

      return ComputeInequal<Dim - 1>::call(*this, other);

      inline auto operator < (const Vector& other) const

      return ComputeLess<Dim - 1>::call(*this, other);

      inline auto operator > (const Vector& other) const

      return ComputeGreater<Dim - 1>::call(*this, other);

      inline auto operator <= (const Vector& other) const

      return ComputeLessOrEqual<Dim - 1>::call(*this, other);

      inline auto operator >= (const Vector& other) const

      return ComputeGreaterOrEqual<Dim - 1>::call(*this, other);

      /**
      * Vector length
      */
      inline auto length() const

      return std::sqrt(length2());

      /**
      * Squared Vector length
      */
      inline auto length2() const

      return dot(*this, *this);

      /**
      * Conversion from/to glm
      */
      inline Vector(const glm::vec<Dim, T>& vec)

      std::memcpy(_data, &vec[0], sizeof vec);

      inline operator glm::vec<Dim, T>() const

      glm::vec<Dim, T> vec;
      std::memcpy(&vec[0], ptr(), sizeof vec);
      return vec;

      /**
      * Change in dimension
      */
      template<unsigned N>
      inline operator Vector<N, T>() const

      Vector<N, T> ret;
      std::memcpy(&ret[0], ptr(), std::min(sizeof ret, sizeof *this));
      return ret;

      /**
      * Debug output
      */
      friend std::ostream& operator << (std::ostream& os, const Vector& vec)

      os << "[";
      for (unsigned i = 0; i < Dim; i++)
      os << vec._data[i];
      if (i != Dim - 1)
      os << " ";


      os << "]";
      return os;

      private:
      T _data[Dim];

      template<unsigned index, unsigned D = Dim, typename Type = T>
      struct ComputeAdd

      static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)

      result[index] = a[index] + b[index];
      ComputeAdd<index - 1, D, Type>::call(a, b, result);

      ;
      template<unsigned D, typename Type>
      struct ComputeAdd<0, D, Type>

      static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)

      result[0] = a[0] + b[0];

      ;
      template<unsigned index, unsigned D = Dim, typename Type = T>
      struct ComputeSub

      static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)

      result[index] = a[index] - b[index];
      ComputeSub<index - 1, D, Type>::call(a, b, result);

      ;
      template<unsigned D, typename Type>
      struct ComputeSub<0, D, Type>

      static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)

      result[0] = a[0] - b[0];

      ;
      template<unsigned index, unsigned D = Dim, typename Type = T>
      struct ComputeMul

      static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)

      result[index] = a[index] * b[index];
      ComputeMul<index - 1, D, Type>::call(a, b, result);

      ;
      template<unsigned D, typename Type>
      struct ComputeMul<0, D, Type>

      static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)

      result[0] = a[0] * b[0];

      ;
      template<unsigned index, unsigned D = Dim, typename Type = T>
      struct ComputeDiv

      static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)

      result[index] = a[index] / b[index];
      ComputeDiv<index - 1, D, Type>::call(a, b, result);

      ;
      template<unsigned D, typename Type>
      struct ComputeDiv<0, D, Type>

      static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)

      result[0] = a[0] / b[0];

      ;
      template<unsigned index, unsigned D = Dim, typename Type = T>
      struct ComputeEqual

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      return a[index] == b[index] && ComputeEqual<index - 1, D, Type>::call(a, b);

      ;
      template<unsigned D, typename Type>
      struct ComputeEqual<0, D, Type>

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      return a[0] == b[0];

      ;
      template<unsigned index, unsigned D = Dim, typename Type = T>
      struct ComputeInequal

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      ;
      template<unsigned D, typename Type>
      struct ComputeInequal<0, D, Type>

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      return a[0] != b[0];

      ;
      template<unsigned index, unsigned D = Dim, typename Type = T>
      struct ComputeLess

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      return a[index] < b[index] && ComputeLess<index - 1, D, Type>::call(a, b);

      ;
      template<unsigned D, typename Type>
      struct ComputeLess<0, D, Type>

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      return a[0] < b[0];

      ;
      template<unsigned index, unsigned D = Dim, typename Type = T>
      struct ComputeGreater

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      return a[index] > b[index] && ComputeGreater<index - 1, D, Type>::call(a, b);

      ;
      template<unsigned D, typename Type>
      struct ComputeGreater<0, D, Type>

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      return a[0] > b[0];

      ;
      template<unsigned index, unsigned D = Dim, typename Type = T>
      struct ComputeLessOrEqual

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      return a[index] <= b[index] && ComputeLessOrEqual<index - 1, D, Type>::call(a, b);

      ;
      template<unsigned D, typename Type>
      struct ComputeLessOrEqual<0, D, Type>

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      return a[0] <= b[0];

      ;
      template<unsigned index, unsigned D = Dim, typename Type = T>
      struct ComputeGreaterOrEqual

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      return a[index] >= b[index] && ComputeGreaterOrEqual<index - 1, D, Type>::call(a, b);

      ;
      template<unsigned D, typename Type>
      struct ComputeGreaterOrEqual<0, D, Type>

      static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)

      return a[0] >= b[0];

      ;
      ;

      using Vec2f = Vector<2, float>;
      using Vec3f = Vector<3, float>;
      using Vec4f = Vector<4, float>;

      using Vec2u = Vector<2, unsigned>;
      using Vec3u = Vector<3, unsigned>;
      using Vec4u = Vector<4, unsigned>;

      using Vec2i = Vector<2, int>;
      using Vec3i = Vector<3, int>;
      using Vec4i = Vector<4, int>;


      #endif


      Edit:
      Definition of the dot product for code completeness:





       template<unsigned Dim, typename T>
      static inline T dot(const Vector<Dim, T>& a, const Vector<Dim, T>& b)

      return ComputeDot<Dim, T, Dim - 1>::call(a, b);


      template<unsigned Dim, typename T, unsigned index>
      struct ComputeDot

      static inline T call(const Vector<Dim, T>& a, const Vector<Dim, T>& b)

      return a[index] * b[index] + ComputeDot<Dim, T, index - 1>::call(a, b);

      ;
      template<unsigned Dim, typename T>
      struct ComputeDot<Dim, T, 0>

      static inline T call(const Vector<Dim, T>& a, const Vector<Dim, T>& b)

      return a[0] * b[0];

      ;






      c++ template template-meta-programming coordinate-system






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited 19 mins ago

























      asked 3 hours ago









      Philipp Fleißner

      434




      434




















          1 Answer
          1






          active

          oldest

          votes

















          up vote
          1
          down vote













          A few issues:



          • Functions defined in the class in the header are inline by default, so there's no need to use the keyword everywhere.

          • Use std::copy, not std::memcpy. It's just as fast (if not faster), and doesn't have memcpy's trivially copyable requirement (currently there's no check to make sure the objects stored in the class are trivially copyable).


          • ComputeAdd etc. could be implemented with simple for loops.

          • Certain operators can be implemented in terms of other operators to save on code duplication (e.g. == / !=).

          • Returning by value with xyz() etc., and then returning a reference with x() etc. is potentially error prone. I'd suggest uniformly returning a copy.

          • It might be useful to have a non-const version of ptr().

          • The default constructor will not initialize values in the array. Is a (potential and minor) performance gain in certain circumstances really worth the massive, omnipresent risk?

          • The template parameter pack constructor will work for numbers of arguments smaller than the dimensions of the vector, and initialize any non-explicitly initialized values implicitly (i.e. to zero). This could be confusing.

          • Conversion operators should be explicit, to prevent unhappy accidents. One argument constructors should also be explicit.

          • A static size() member function to return the number of dimensions might be useful.

          • Iterator support might be useful.


          • Using std::array instead of a raw c-style array might fix some of the above or make implementing them (e.g. iterators) easier...





          share|improve this answer






















            Your Answer




            StackExchange.ifUsing("editor", function ()
            return StackExchange.using("mathjaxEditing", function ()
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            );
            );
            , "mathjax-editing");

            StackExchange.ifUsing("editor", function ()
            StackExchange.using("externalEditor", function ()
            StackExchange.using("snippets", function ()
            StackExchange.snippets.init();
            );
            );
            , "code-snippets");

            StackExchange.ready(function()
            var channelOptions =
            tags: "".split(" "),
            id: "196"
            ;
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function()
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled)
            StackExchange.using("snippets", function()
            createEditor();
            );

            else
            createEditor();

            );

            function createEditor()
            StackExchange.prepareEditor(
            heartbeatType: 'answer',
            convertImagesToLinks: false,
            noModals: false,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            );



            );













             

            draft saved


            draft discarded


















            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f205553%2fc-math-vector-template%23new-answer', 'question_page');

            );

            Post as a guest






























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes








            up vote
            1
            down vote













            A few issues:



            • Functions defined in the class in the header are inline by default, so there's no need to use the keyword everywhere.

            • Use std::copy, not std::memcpy. It's just as fast (if not faster), and doesn't have memcpy's trivially copyable requirement (currently there's no check to make sure the objects stored in the class are trivially copyable).


            • ComputeAdd etc. could be implemented with simple for loops.

            • Certain operators can be implemented in terms of other operators to save on code duplication (e.g. == / !=).

            • Returning by value with xyz() etc., and then returning a reference with x() etc. is potentially error prone. I'd suggest uniformly returning a copy.

            • It might be useful to have a non-const version of ptr().

            • The default constructor will not initialize values in the array. Is a (potential and minor) performance gain in certain circumstances really worth the massive, omnipresent risk?

            • The template parameter pack constructor will work for numbers of arguments smaller than the dimensions of the vector, and initialize any non-explicitly initialized values implicitly (i.e. to zero). This could be confusing.

            • Conversion operators should be explicit, to prevent unhappy accidents. One argument constructors should also be explicit.

            • A static size() member function to return the number of dimensions might be useful.

            • Iterator support might be useful.


            • Using std::array instead of a raw c-style array might fix some of the above or make implementing them (e.g. iterators) easier...





            share|improve this answer


























              up vote
              1
              down vote













              A few issues:



              • Functions defined in the class in the header are inline by default, so there's no need to use the keyword everywhere.

              • Use std::copy, not std::memcpy. It's just as fast (if not faster), and doesn't have memcpy's trivially copyable requirement (currently there's no check to make sure the objects stored in the class are trivially copyable).


              • ComputeAdd etc. could be implemented with simple for loops.

              • Certain operators can be implemented in terms of other operators to save on code duplication (e.g. == / !=).

              • Returning by value with xyz() etc., and then returning a reference with x() etc. is potentially error prone. I'd suggest uniformly returning a copy.

              • It might be useful to have a non-const version of ptr().

              • The default constructor will not initialize values in the array. Is a (potential and minor) performance gain in certain circumstances really worth the massive, omnipresent risk?

              • The template parameter pack constructor will work for numbers of arguments smaller than the dimensions of the vector, and initialize any non-explicitly initialized values implicitly (i.e. to zero). This could be confusing.

              • Conversion operators should be explicit, to prevent unhappy accidents. One argument constructors should also be explicit.

              • A static size() member function to return the number of dimensions might be useful.

              • Iterator support might be useful.


              • Using std::array instead of a raw c-style array might fix some of the above or make implementing them (e.g. iterators) easier...





              share|improve this answer
























                up vote
                1
                down vote










                up vote
                1
                down vote









                A few issues:



                • Functions defined in the class in the header are inline by default, so there's no need to use the keyword everywhere.

                • Use std::copy, not std::memcpy. It's just as fast (if not faster), and doesn't have memcpy's trivially copyable requirement (currently there's no check to make sure the objects stored in the class are trivially copyable).


                • ComputeAdd etc. could be implemented with simple for loops.

                • Certain operators can be implemented in terms of other operators to save on code duplication (e.g. == / !=).

                • Returning by value with xyz() etc., and then returning a reference with x() etc. is potentially error prone. I'd suggest uniformly returning a copy.

                • It might be useful to have a non-const version of ptr().

                • The default constructor will not initialize values in the array. Is a (potential and minor) performance gain in certain circumstances really worth the massive, omnipresent risk?

                • The template parameter pack constructor will work for numbers of arguments smaller than the dimensions of the vector, and initialize any non-explicitly initialized values implicitly (i.e. to zero). This could be confusing.

                • Conversion operators should be explicit, to prevent unhappy accidents. One argument constructors should also be explicit.

                • A static size() member function to return the number of dimensions might be useful.

                • Iterator support might be useful.


                • Using std::array instead of a raw c-style array might fix some of the above or make implementing them (e.g. iterators) easier...





                share|improve this answer














                A few issues:



                • Functions defined in the class in the header are inline by default, so there's no need to use the keyword everywhere.

                • Use std::copy, not std::memcpy. It's just as fast (if not faster), and doesn't have memcpy's trivially copyable requirement (currently there's no check to make sure the objects stored in the class are trivially copyable).


                • ComputeAdd etc. could be implemented with simple for loops.

                • Certain operators can be implemented in terms of other operators to save on code duplication (e.g. == / !=).

                • Returning by value with xyz() etc., and then returning a reference with x() etc. is potentially error prone. I'd suggest uniformly returning a copy.

                • It might be useful to have a non-const version of ptr().

                • The default constructor will not initialize values in the array. Is a (potential and minor) performance gain in certain circumstances really worth the massive, omnipresent risk?

                • The template parameter pack constructor will work for numbers of arguments smaller than the dimensions of the vector, and initialize any non-explicitly initialized values implicitly (i.e. to zero). This could be confusing.

                • Conversion operators should be explicit, to prevent unhappy accidents. One argument constructors should also be explicit.

                • A static size() member function to return the number of dimensions might be useful.

                • Iterator support might be useful.


                • Using std::array instead of a raw c-style array might fix some of the above or make implementing them (e.g. iterators) easier...






                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited 15 mins ago

























                answered 21 mins ago









                user673679

                1,505620




                1,505620



























                     

                    draft saved


                    draft discarded















































                     


                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function ()
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f205553%2fc-math-vector-template%23new-answer', 'question_page');

                    );

                    Post as a guest













































































                    Comments

                    Popular posts from this blog

                    What does second last employer means? [closed]

                    Installing NextGIS Connect into QGIS 3?

                    One-line joke