C++20 Vector2D Template Pretty simple 2-Dimensional Vector template with operators and two utility functions making use of C++20 concepts. Header-only templates, function inlining and operator overloading, etc. is not exactly my strong foot, so that's why i wrote this hoping to get some feedback.
#pragma once

#include &lt;cmath&gt;
#include &lt;type_traits&gt;

namespace math {

    template&lt;typename T&gt;
    concept Arithmetic = std::is_arithmetic_v&lt;T&gt;;

    template&lt;Arithmetic T&gt;
    struct Vector2D
    {
        T X = 0;
        T Y = 0;

        Vector2D() = default;

        Vector2D(T x, T y);

        Vector2D(const Vector2D&lt;T&gt; &amp;other);

        inline T Magnitude() const;

        inline Vector2D&lt;T&gt; Normal() const;
    };

    template&lt;Arithmetic T&gt;
    inline Vector2D&lt;T&gt;::Vector2D(T x, T y) : X(x), Y(y)
    {}

    template&lt;Arithmetic T&gt;
    inline Vector2D&lt;T&gt;::Vector2D(const Vector2D&lt;T&gt; &amp;other) : X(other.X), Y(other.Y)
    {}

    template&lt;Arithmetic T&gt;
    inline T Vector2D&lt;T&gt;::Magnitude() const
    {
        return std::sqrt(X * X + Y * Y);
    }

    template&lt;Arithmetic T&gt;
    inline Vector2D&lt;T&gt; Vector2D&lt;T&gt;::Normal() const
    {
        auto magnitude = Magnitude();

        return Vector2D&lt;T&gt;(X / magnitude, Y / magnitude);
    }

    template&lt;Arithmetic T&gt;
    inline Vector2D&lt;T&gt; operator-(const Vector2D&lt;T&gt; &amp;vec)
    {
        return Vector2D&lt;T&gt;(-vec.X, -vec.Y);
    }

    template&lt;Arithmetic T&gt;
    inline Vector2D&lt;T&gt; operator+(const Vector2D&lt;T&gt; &amp;first, const Vector2D&lt;T&gt; &amp;second)
    {
        return Vector2D&lt;T&gt;(first.X + second.X, first.Y + second.Y);
    }

    template&lt;Arithmetic T&gt;
    inline Vector2D&lt;T&gt; operator-(const Vector2D&lt;T&gt; &amp;first, const Vector2D&lt;T&gt; &amp;second)
    {
        return Vector2D&lt;T&gt;(first.X + second.X, first.Y + second.Y);
    }

    template&lt;Arithmetic T&gt;
    inline Vector2D&lt;T&gt; operator*(const Vector2D&lt;T&gt; &amp;vec, const T factor)
    {
        return Vector2D&lt;T&gt;(vec.X * factor, vec.Y * factor);
    }

    template&lt;Arithmetic T&gt;
    inline Vector2D&lt;T&gt; operator*(const T factor, const Vector2D&lt;T&gt; &amp;vec)
    {
        return Vector2D&lt;T&gt;(factor * vec.X, factor * vec.Y);
    }

    template&lt;Arithmetic T&gt;
    inline Vector2D&lt;T&gt; operator/(const Vector2D&lt;T&gt; &amp;vec, const T divisor)
    {
        return Vector2D&lt;T&gt;(vec.X / divisor, vec.Y / divisor);
    }

    template&lt;Arithmetic T&gt;
    inline Vector2D&lt;T&gt; &amp;operator+=(Vector2D&lt;T&gt; &amp;first, const Vector2D&lt;T&gt; &amp;second)
    {
        first.X += second.X;
        first.Y += second.Y;

        return first;
    }

    template&lt;Arithmetic T&gt;
    inline Vector2D&lt;T&gt; &amp;operator-=(Vector2D&lt;T&gt; &amp;first, const Vector2D&lt;T&gt; &amp;second)
    {
        first.X -= second.X;
        first.Y -= second.Y;

        return first;
    }

    template&lt;Arithmetic T&gt;
    inline Vector2D&lt;T&gt; &amp;operator*=(Vector2D&lt;T&gt; &amp;vec, const T factor)
    {
        vec.X *= factor;
        vec.Y *= factor;

        return vec;
    }

    template&lt;Arithmetic T&gt;
    inline Vector2D&lt;T&gt; &amp;operator/=(Vector2D&lt;T&gt; &amp;vec, const T factor)
    {
        vec.X /= factor;
        vec.Y /= factor;

        return vec;
    }

    template&lt;Arithmetic T&gt;
    inline Vector2D&lt;T&gt; &amp;operator==(const Vector2D&lt;T&gt; &amp;first, const Vector2D&lt;T&gt; &amp;second)
    {
        return (first.X == second.X) &amp;&amp; (second.Y == second.Y);
    }

    template&lt;Arithmetic T&gt;
    inline Vector2D&lt;T&gt; &amp;operator!=(const Vector2D&lt;T&gt; &amp;first, const Vector2D&lt;T&gt; &amp;second)
    {
        return !(first == second);
    }
}
Overview
Your code as written is perfectly fine (apart from the one bug in subtraction). Anything mentioned here is mainly to help future readers.
You overcomplicated the design by adding constructors to Vector2D.
The default constructors work perfectly and as expected in this type of situation.
There is an argument to make operators members of the class rather than free standing functions. But either work.
Your use of reference marker &amp; is very C like. You put it next to the variable rather than next to the type. This is very subjective style thing (usually dictated by your style guide). But C++ guides skew one way while C guides skew the other.
 void doStuff(C const&amp; param)
              ^^^^^^^^   param has a type: C const&amp;

 void doStuff(C&amp; param)
              ^^         param has a type: C&amp;

This has happened because in C++ types are much more significant and important. So we pay much more attention to type things in C++.
Code Review
I am going to have to take your word that this is how concepts work. I look forward to this becoming available more consistently.
    template&lt;typename T&gt;
    concept Arithmetic = std::is_arithmetic_v&lt;T&gt;;

    template&lt;Arithmetic T&gt;
    struct Vector2D
    {


Sure but not needed if you don't define other constructors (see below).
        Vector2D() = default;

Sure but not needed as the brace initializers works and does this for you:
        Vector2D(T x, T y);

Sure: But not needed the default copy constructor should work here.
        Vector2D(const Vector2D&lt;T&gt; &amp;other);

Don't need to declare these inline here. It has no effect. You put the inline next to the method definition to make sure that the compiler does not give you multiple definition warnings.
        inline T Magnitude() const;
        inline Vector2D&lt;T&gt; Normal() const;
    };

I would note that the inline keyword has nothing to do with in-lining the code. The compiler decides that without any help from the engineer (because it is better at than you).
I would have simplified that to:
    template&lt;Arithmetic T&gt;
    struct Vector2D
    {
        T  x;
        T  y;

        T Magnitude() const;
        Vector2D&lt;T&gt; Normal() const;
    };


Arithmetic Operators:
There is a debate about if these should be members or free standing functions. Either is fine. I will not ding you for doing it one way when I would have done it another.
There is an argument for making them free standing functions. But this is usually based around symmetry when using the operators.
   R = X + Y;

If you use methods:
If X is Vector2D and Y is not then the compiler can potentially convert Y into Vector2D and still apply the operation. But if it is the other way around: Y is Vector2D and X is not then the compiler can not convert X to a Vector2D.
If you use free standing functions:
Then if one parameter is Vector2D then the other can be converted to Vector2D and the function applied. This you have symmetrical conversions.
This is fine if the types are &quot;Arithmetic&quot; in nature and can easily convert from anything into your type; this is not the case here with Vector2D. So this argument does not stand. I would also argue that this applies to most types; you have to have a very good argument that your type should allow auto conversion (most types don't and use explicit on one argument constructors to prevent this).
Thus here you can use either technique and both are fine.
I personally would have made them members of the class (the sole reason is to add clarity so you can read the class and see all the things you can do to change them). You could argue you can have the same affect by adding all the declarations for arithmetic operations as friends to the class (or simply put the declarations directly under the class (and I could not argue against his).
Whatever way you decide. I would put the declarations beside the class and away from the definition so it is easy to see all the possible operations that apply to the class.

Normally we implement operator+ in terms of operator+=.
    template&lt;Arithmetic T&gt;
    inline Vector2D&lt;T&gt; operator+(const Vector2D&lt;T&gt; &amp;first, const Vector2D&lt;T&gt; &amp;second)
    {
        return Vector2D&lt;T&gt;(first.X + second.X, first.Y + second.Y);
    }

I would write like this:
    template&lt;Arithmetic T&gt;
    inline Vector2D&lt;T&gt;&amp; operator+=(Vector2D&lt;T&gt;&amp; first, Vector2D&lt;T&gt; const&amp; second)
    {
        first.X += second.X;
        first.Y += second.Y;
        return first;
    }

    template&lt;Arithmetic T&gt;
    inline Vector2D&lt;T&gt; operator+(Vector2D&lt;T&gt; first, Vector2D&lt;T&gt; const&amp; second)
    {
        // Note Pass first by value.
        // This automatically gets us a new version of the object.
        // The compiler will be able to detect if we can use move/copy
        // automatically to get this move version.
        //
        // We can then do the += on this new copy.
        // and perfectly return this value as output.
        return first += second;
    }


This seems like a bug:
    template&lt;Arithmetic T&gt;
    inline Vector2D&lt;T&gt; operator-(const Vector2D&lt;T&gt; &amp;first, const Vector2D&lt;T&gt; &amp;second)
    {
        // Should you not subtract here:
        return Vector2D&lt;T&gt;(first.X + second.X, first.Y + second.Y);
    }