Copy ellision will only happen for variables instantiated in the method. Thar is due to how copy ellision. The caller will make space for the return value when it calls the callee. But in order to use that space, the callee will have to create a variable using this space in its definition (theoretically it maybe could direct the copy that is made in the paramter (since it is passed by value) to this space vut compilers aren't that good yet)) Source: A cppcon talk about copy ellision.
That a move constructor exists will not give you copy ellision, but if copy ellision is impossible, the compiler will first try to move and then to copy if move is impossible. So the existence of a move constructor will probably improve the speed if there is no copy ellision.
You should never return a temporary (i.e. a variable going out of scope at the end of the function) by std::move
since it prevents copy ellision and even if the copy ellision is not possible, the compiler will move by default. The only reason (I can think of) to return by mkve is, if you are releasing a resource the object held before the call. For example std::unique_ptr::release
should return by move, iirc.