In general you use std::move when you want to make a copy but you no longer care about the object that you're copying from so you move it which can sometimes be more efficient.
The standard says move constructors and move assignment operators for classes in the standard library will leave the object in a valid but unspecified state unless otherwise specified. For your implementation of std::string it happened to leave the string empty but this is nothing to rely on in portable code.
int is a built-in type so moving is just the same as copying. There is no way to make it faster.
Some types are so called "move-only types" that cannot be copied, only moved.
Move-only types often specify what will happen if you move from them.
An example of a move-only type is std::unique_ptr. Moving from a std::unique_ptr puts it in an empty (null) state.
For copyable types it's more error-prone to rely on the state of something after moving from it, even if you happen to know what that is, because the language has been designed so that things fall back to copying if moving is not possible.
Example:
1 2 3
const T obj = ...
...
T obj2 = std::move(obj); // Oops! This will copy because obj is const.
Move-only types don't have this problem because if moving fails it will give you a compilation error.
Note that std::move does not actually "move" anything. It is the operator/function that you pass the result to that is responsible for doing the "moving".
std::move() returns a r-value ref type from the passed type. It does nothing else. What is done with a r-value type depends upon how it's used and whether what's it's used with differentiates between l-value and r-value types.