Comment on the style of this C++ class for a 2D Vector 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 &lt;typename T&gt;
class Vector2D final {
public:
    T x;
    T y;

    // Defaulted constructors
    Vector2D(const Vector2D&lt;T&gt;&amp; v) = default;
    ~Vector2D() = default;

    Vector2D(T ix, T iy);

    // Member Utility Functions
    void Set(T ix, T iy);
    T Magnitude();

    // Overloaded Compound Operators
    Vector2D&lt;T&gt;&amp; operator+=(const Vector2D&lt;T&gt;&amp; rhs);
    Vector2D&lt;T&gt;&amp; operator*=(const Vector2D&lt;T&gt;&amp; rhs);
    Vector2D&lt;T&gt;&amp; operator-=(const Vector2D&lt;T&gt;&amp; rhs);
    Vector2D&lt;T&gt;&amp; operator/=(const T&amp; rhs);
    Vector2D&lt;T&gt;&amp; operator=(const  Vector2D&lt;T&gt;&amp; rhs);
};

// Non-Compound Operators.
template &lt;typename T&gt; Vector2D&lt;T&gt; operator+(Vector2D&lt;T&gt; lhs, const Vector2D&lt;T&gt;&amp; rhs);
template &lt;typename T&gt; Vector2D&lt;T&gt; operator*(Vector2D&lt;T&gt; lhs, const Vector2D&lt;T&gt;&amp; rhs);
template &lt;typename T&gt; Vector2D&lt;T&gt; operator-(Vector2D&lt;T&gt; lhs, const Vector2D&lt;T&gt;&amp; rhs);
template &lt;typename T&gt; Vector2D&lt;T&gt; operator/(Vector2D&lt;T&gt; lhs, const T&amp; rhs);

template &lt;typename T&gt;
Vector2D&lt;T&gt;::Vector2D(T ix, T iy)
        : x(ix)
        , y(iy)
{
}

template &lt;typename T&gt;
void Vector2D&lt;T&gt;::Set(T ix, T iy) {
    x = ix;
    y = iy;
}

template &lt;typename T&gt;
T Vector2D&lt;T&gt;::Magnitude() {
    return x*x + y*y;
}

template &lt;typename T&gt;
Vector2D&lt;T&gt;&amp; Vector2D&lt;T&gt;::operator+=(const Vector2D&lt;T&gt;&amp; rhs) {
    x += rhs.x;
    y += rhs.y;
    return *this;
}

template &lt;typename T&gt;
Vector2D&lt;T&gt;&amp; Vector2D&lt;T&gt;::operator*=(const Vector2D&lt;T&gt;&amp; rhs) {
    x *= rhs.x;
    y *= rhs.y;
    return *this;
}

template &lt;typename T&gt;
Vector2D&lt;T&gt;&amp; Vector2D&lt;T&gt;::operator-=(const Vector2D&lt;T&gt;&amp; rhs) {
    x -= rhs.x;
    y -= rhs.y;
    return *this;
}

template &lt;typename T&gt;
Vector2D&lt;T&gt;&amp; Vector2D&lt;T&gt;::operator/=(const T&amp; rhs) {
    x /= rhs;
    y /= rhs;
    return *this;
}

template &lt;typename T&gt;
Vector2D&lt;T&gt;&amp; Vector2D&lt;T&gt;::operator=(const Vector2D&lt;T&gt;&amp; rhs) {
    x = rhs.x;
    y = rhs.y;
    return *this;
}

template &lt;typename T&gt;
Vector2D&lt;T&gt; operator+(Vector2D&lt;T&gt; lhs, const Vector2D&lt;T&gt;&amp; rhs) {
    lhs += rhs;
    return lhs;
}

template &lt;typename T&gt;
Vector2D&lt;T&gt; operator*(Vector2D&lt;T&gt; lhs, const Vector2D&lt;T&gt;&amp; rhs) {
    lhs *= rhs;
    return lhs;
}

template &lt;typename T&gt;
Vector2D&lt;T&gt; operator-(Vector2D&lt;T&gt; lhs, const Vector2D&lt;T&gt;&amp; rhs) {
    lhs -= rhs;
    return lhs;
}

template &lt;typename T&gt;
Vector2D&lt;T&gt; operator/(Vector2D&lt;T&gt; lhs, const T&amp; 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&lt;float&gt; v1 = {1.0, 2.0};
Vector2D&lt;float&gt; v2 = {2.0, 3.0};

v1 += v2;
assert(v1.x == 3.0 &amp;&amp; v1.y == 5.0);
v1 -= v2;
assert(v1.x == 1.0 &amp;&amp; v1.y == 2.0);
v1 *= v2;
assert(v1.x == 2.0 &amp;&amp; v1.y == 6.0);
v1 /= 2.0;
assert(v1.x == 1.0 &amp;&amp; v1.y == 3.0);

v1.Set(1.0, 2.0);
v2.Set(2.0, 3.0);
Vector2D&lt;float&gt; v3 = {0.0, 0.0};

v3 = v1 + v2;
assert(v3.x == 3.0 &amp;&amp; v3.y == 5.0);
v3 = v1 - v2;
assert(v3.x == -1.0 &amp;&amp; v3.y == -1.0);
v3 = v1 * v2;
assert(v3.x == 2.0 &amp;&amp; v3.y == 6.0);
v3 = v1 / 2.0f;
assert(v3.x == 0.5 &amp;&amp; v3.y == 1.0);
This looks pretty good! I don't think you need to change much, but there are some things missing that you will likely need:


normalize() or normal() method to either normalize a vector or return a normalized version of the vector. If you're doing physics, you'll need normalized vectors for lots of operations, probably.
Scalar multiply (operator*(const T)). You'll likely need to scale vectors frequently and will do that by multiplying by a scalar.
operator==() and/or operator!=(). You should define one in terms of the other.
Possibly a squared magnitude. Sometimes taking the square root in the Magnitude() function is a bottleneck and you don't need it (for example to see if a point is within a given radius of another point). It can be faster to get the squared magnitude of the vector between the two points if that's something you do a lot.