Disclaimer: ChatGPT generated document.
The key point is that std::move does not actually move anything, and it doesn't create a new object. It simply casts an expression to an rvalue reference, allowing move constructors or move assignment operators to be selected.
A simplified implementation looks like this:
template <typename T>
constexpr std::remove_reference_t<T>&& move(T&& t) noexcept {
return static_cast<std::remove_reference_t<T>&&>(t);
}Let's break that down.
The parameter is a forwarding reference:
T&& tSuppose you have
std::string s = "hello";and call
std::move(s);Template deduction gives
T = std::string&because s is an lvalue.
So the parameter type becomes
std::string& && // collapses to std::string&Inside the function, t is simply a reference to s.
std::remove_reference_t<T> turns
std::string&into
std::stringNow we have
std::string&&The return statement is
static_cast<std::string&&>(t)This tells the compiler:
"Treat this object as an xvalue (an expiring value)."
No object is created. No bytes are copied. The object is still s.
std::string s = "hello";
std::string t = std::move(s);Conceptually, this becomes
std::string t = static_cast<std::string&&>(s);Because the initializer is now an rvalue, overload resolution prefers the move constructor:
std::string(std::string&&);instead of
std::string(const std::string&);The C++ language classifies expressions into value categories:
- lvalue — has identity (
s) - prvalue — temporary (
std::string("hello")) - xvalue — expiring object (
std::move(s))
static_cast<T&&>(obj) changes the expression's value category to an xvalue.
So after
auto&& x = std::move(s);x refers to the same object as s, but the expression std::move(s) is an xvalue, making move overloads eligible.
Although std::move(s) has type std::string&&, if you bind it to a named variable:
auto&& r = std::move(s);then r itself is an lvalue expression, because every named variable is an lvalue.
So:
foo(r); // calls lvalue overload
foo(std::move(r)); // calls rvalue overloadThis often surprises people.
std::move is essentially just:
template <typename T>
constexpr std::remove_reference_t<T>&& move(T&& t) noexcept {
return static_cast<std::remove_reference_t<T>&&>(t);
}It:
- Accepts any object by reference.
- Removes any existing reference qualifiers.
- Casts the expression to an rvalue reference using
static_cast<T&&>.
It does not move data itself. The actual move occurs only if a move constructor or move assignment operator is invoked on the resulting xvalue.
