A primary problem here is that you wrote an operator= at all. Detailed explanation of why this is a problem:
In C++, the assignment operators are generated automatically by default. These automatically-generated operators have _special properties_. Namely, they can be what is called a "trivial" operator. If you write your own operator - even an empty one! - it is not _trivial_.
The importance is that the trivial operator allows the compiler or library in some cases to generate wildly different (and more optimal) code.
For an assignment operator, the difference between a trivial version and a non-trivial version is that the trivial assignment will just copy memory around while the non-trivial one is a function. Even if your implementation of the assignment operator is just copying memory, it's still a function that copies memory, and not just a magical automatic copy of memory.
Functions aren't free to call; there's some overhead involve in how the CPU has to setup a function to be called, overhead in the function "prolog", more overhead in the function "epilog", and yet more overhead in the caller's code that cleans up after a function is called.
This function call overhead is far worse in Debug builds or when using various default options in many compilers (including Microsoft's). For security purposes, the compiler might silently insert additional "safety check" code into a function prolog or epilog. This results in small do-nothing functions actually have _very large_ overhead relative to the work that they actually do.
Function call overhead is a primary reason that "function inlining" is such an important optimization that is turned on in Release builds (or that you can turn on manually yourself in Debug builds, which is what I do, because I need my Debug builds to be fast enough for my game to be interactive and not a slideshow).
If you want to guarantee that one of the default operators exists with its default semantics (of just copying memory), you can declare a "defaulted function" using the =default syntax, e.g.
class vec2 {
float x, y;
vec2& operator=(vec2 const&) = default;
};
In the above snippet, the default operator declaration serves no purpose because the compiler would have generated it anyway. There are various things you might do to suppress the default generation of functions, though, so you need to use the =default syntax to turn that generation back on. Importantly, defaulted functions are the _only_ way to get a "trivial" operation for constructors, destructors, or assignment operators. The most common use of =default I see is generally to enable the default constructor in cases that you also have a user-provided constructor, which for your vector would be:
class vec2 {
float x, y;
explicit vec2(float x, float y) : x(x), y(y) {} // user-provided constructor, so the default constructor is suppressed!
vec2() = default; // tell the compiler to generate the default (trivial) constructor anyway
};
Note that the trivial constructor does _no_ initialization, meaning that in the above code the .x and .y members of vec2 will be _uninitialized memory_ if the default constructor is invoked. This may not be what you want and you may want to provide a non-trivial default constructor that zero-initializes those members.
As an additional note, there is also an =delete modifier you can use on function to _suppress_ the default generation in cases where you don't want the operator to exist at all. e.g., if you wanted a class to not be copyable at all:
class not_copyable {
not_copyable(not_copyable const&) = delete; // disbale copy construction (but not copy assignment) entirely
not_copyable& operator=(not_copyable const&) = delete; // disable copy assignment (but not copy construction) entirely
};