The move constructor is used when constructing an object from an rvalue. It's not necessarily used to construct the rvalue itself. In many situations the compiler is smart enough to not have to call the move constructor at all.
1. V v1 = V{};
V{} is an rvalue so it is possible that v1 will be constructed using the move constructor but modern compilers are smart enough to optimize this code as if you had written V v1{};. In C++17 the compiler isn't even allowed to call the move constructor in this situation.
2. V{};
This always calls the default constructor because you don't pass any arguments to the constructor.
3. getV();
Again, the compiler is smart enough to avoid creating a second object. Instead the function constructs the local variable in place where the return value should be. The compiler would have been able to do this even if you had written something like V v2 = getV();. This is not guaranteed by the standard but it's a relatively easy thing for the compiler to do so there is no excuse for the compiler not to do it.
4. int number = getV().getInt();
This is just like #3. That the returned object is being used doesn't change anything.
To force the copy/move constructor to be called, disable NRVO.
1 2 3 4 5 6 7 8 9
V getV() {
V localV;
// *** clang++ warning: moving a local object in a return statement prevents copy elision
// note: remove std::move call here
return std::move(localV); // deliberately disable NRVO
// force move construction of result
}