Reading — step 1 of 7
Learn
Move semantics (C++11+) lets you transfer ownership of resources without copying — turning expensive operations into cheap ones. Built on rvalue references (T&&).
The motivating problem
std::vector<int> bigVec(1'000'000);
std::vector<int> v = bigVec; // ❌ deep copy of 1M ints
std::vector<int> v2 = std::move(bigVec); // ✓ move — cheap pointer transfer
Returning by value, passing arguments — many operations would be expensive copies if every assignment had to duplicate. Move semantics says: if the source is about to die anyway, just steal its insides.
lvalues vs rvalues
- lvalue — has an identity, you can take its address. Variables, references, anything you can put on the left of
=. - rvalue — temporary, no name. Function return values, literals, expression results.
int x = 5; // x is an lvalue; 5 is an rvalue
int y = x + 1; // x is lvalue; (x + 1) is rvalue (a temporary)
int& lref = x; // lvalue reference — binds to lvalues
int&& rref = 5; // rvalue reference — binds to rvalues
int&& rref2 = std::move(x); // turns lvalue x INTO an rvalue
Move constructors and assignment
A move constructor takes an rvalue reference of its own type:
class Buffer {
public:
Buffer(Buffer&& other) noexcept
: data_(other.data_), size_(other.size_)
{
other.data_ = nullptr; // STEAL — leave other in valid empty state
other.size_ = 0;
}
private:
int* data_;
size_t size_;
};
The key: COPY just the pointer/handle, then NULL OUT the source's pointer so its destructor doesn't free what we just stole.
std::move — "convert to rvalue"
std::move(x) doesn't actually move — it CASTS x to an rvalue reference, signaling "feel free to steal from me."
std::string a = "hello";
std::string b = std::move(a); // calls move ctor; a is now empty
// using a here is valid but the value is unspecified — typically empty
After move, the source is in a valid but unspecified state. You can assign to it or destroy it; you shouldn't read its value.
When does move happen automatically?
The compiler picks move over copy in two main places:
- Returning a local by value (Return Value Optimization or implicit move):
std::vector<int> makeVec() {
std::vector<int> v = {1, 2, 3};
return v; // moves (or RVO elides the copy entirely)
}
- Function argument is an rvalue:
void take(std::vector<int>);
take(std::vector<int>{1, 2, 3}); // argument is a temporary → moves in
Pass-by-value with move — the modern idiom
class User {
public:
User(std::string name) : name_(std::move(name)) {} // move from the parameter
private:
std::string name_;
};
User u("Alice"); // string created from literal, moved into name_
std::string s = "Bob";
User u2(s); // copy s into parameter, then move parameter into name_
User u3(std::move(s)); // move s into parameter, then move parameter into name_
One function signature handles all cases efficiently. Modern C++ best practice.
Common mistakes
- Using a moved-from object — valid but unspecified state. Don't read; reassign first.
- Forgetting
noexcepton move ops — STL containers may fall back to copy. std::moveon a const value — silently doesn't move (can't modify const). Move ctors take non-const rvalue ref.- Returning
std::move(local)— usually inhibits RVO, slightly worse. Justreturn local;lets the compiler pick.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…