I wrote a template class for a 2D vector that is intended to provide 2D vector arithmetic to be used in a Physics simulation program. Note that x and y are public or ease of use since encapsulation here would just decrease the readability of code that uses the class.
Is the general style of my code good and does it comply with modern C++ best practices? Thank you for the suggestions.
template <typename T>
class Vector2D final {
public:
T x;
T y;
// Defaulted constructors
Vector2D(const Vector2D<T>& v) = default;
~Vector2D() = default;
Vector2D(T ix, T iy);
// Member Utility Functions
void Set(T ix, T iy);
T Magnitude();
// Overloaded Compound Operators
Vector2D<T>& operator+=(const Vector2D<T>& rhs);
Vector2D<T>& operator*=(const Vector2D<T>& rhs);
Vector2D<T>& operator-=(const Vector2D<T>& rhs);
Vector2D<T>& operator/=(const T& rhs);
Vector2D<T>& operator=(const Vector2D<T>& rhs);
};
// Non-Compound Operators.
template <typename T> Vector2D<T> operator+(Vector2D<T> lhs, const Vector2D<T>& rhs);
template <typename T> Vector2D<T> operator*(Vector2D<T> lhs, const Vector2D<T>& rhs);
template <typename T> Vector2D<T> operator-(Vector2D<T> lhs, const Vector2D<T>& rhs);
template <typename T> Vector2D<T> operator/(Vector2D<T> lhs, const T& rhs);
template <typename T>
Vector2D<T>::Vector2D(T ix, T iy)
: x(ix)
, y(iy)
{
}
template <typename T>
void Vector2D<T>::Set(T ix, T iy) {
x = ix;
y = iy;
}
template <typename T>
T Vector2D<T>::Magnitude() {
return x*x + y*y;
}
template <typename T>
Vector2D<T>& Vector2D<T>::operator+=(const Vector2D<T>& rhs) {
x += rhs.x;
y += rhs.y;
return *this;
}
template <typename T>
Vector2D<T>& Vector2D<T>::operator*=(const Vector2D<T>& rhs) {
x *= rhs.x;
y *= rhs.y;
return *this;
}
template <typename T>
Vector2D<T>& Vector2D<T>::operator-=(const Vector2D<T>& rhs) {
x -= rhs.x;
y -= rhs.y;
return *this;
}
template <typename T>
Vector2D<T>& Vector2D<T>::operator/=(const T& rhs) {
x /= rhs;
y /= rhs;
return *this;
}
template <typename T>
Vector2D<T>& Vector2D<T>::operator=(const Vector2D<T>& rhs) {
x = rhs.x;
y = rhs.y;
return *this;
}
template <typename T>
Vector2D<T> operator+(Vector2D<T> lhs, const Vector2D<T>& rhs) {
lhs += rhs;
return lhs;
}
template <typename T>
Vector2D<T> operator*(Vector2D<T> lhs, const Vector2D<T>& rhs) {
lhs *= rhs;
return lhs;
}
template <typename T>
Vector2D<T> operator-(Vector2D<T> lhs, const Vector2D<T>& rhs) {
lhs -= rhs;
return lhs;
}
template <typename T>
Vector2D<T> operator/(Vector2D<T> lhs, const T& rhs) {
lhs /= rhs;
return lhs;
}
Some examples of using this with float. I intend to only use it with floating point types (float, double, long double etc) and have tested it with other types but will present float here for brevity.
Vector2D<float> v1 = {1.0, 2.0};
Vector2D<float> v2 = {2.0, 3.0};
v1 += v2;
assert(v1.x == 3.0 && v1.y == 5.0);
v1 -= v2;
assert(v1.x == 1.0 && v1.y == 2.0);
v1 *= v2;
assert(v1.x == 2.0 && v1.y == 6.0);
v1 /= 2.0;
assert(v1.x == 1.0 && v1.y == 3.0);
v1.Set(1.0, 2.0);
v2.Set(2.0, 3.0);
Vector2D<float> v3 = {0.0, 0.0};
v3 = v1 + v2;
assert(v3.x == 3.0 && v3.y == 5.0);
v3 = v1 - v2;
assert(v3.x == -1.0 && v3.y == -1.0);
v3 = v1 * v2;
assert(v3.x == 2.0 && v3.y == 6.0);
v3 = v1 / 2.0f;
assert(v3.x == 0.5 && v3.y == 1.0);