Move Semantics, Rvalue References, and the Rule of Five
Master modern C++ resource ownership: move constructors, move assignment operators, noexcept contracts, std::move casts, and the canonical Rule of Five.
By the end, you’ll be able to…
- Synthesize move constructors and move assignment operators that transfer heap ownership in O(1) time
- Explain why move constructors must be marked noexcept to enable std::vector reallocation guarantees
- Apply the Rule of Five (Destructor, Copy Ctor, Copy Assign, Move Ctor, Move Assign) and Rule of Zero
01The Motivation for Move Semantics
Prior to C++11, returning a large container (such as a 1GB std::vector or large string) from a function or passing a temporary into a collection required a deep copy. A deep copy allocates a brand-new heap buffer, copies millions of bytes across the CPU memory bus, and then immediately destroys the original temporary object—wasting millions of clock cycles.
C++11 introduced Move Semantics via Rvalue References (T&&). Instead of copying resources from an object that is about to be destroyed (a temporary or expiring xvalue), we can 'steal' its resources in O(1) time.
A move operation simply copies the internal raw pointer address from the source object, updates size fields, and resets the source object's pointer to nullptr. When the source object later destructs, delete[] nullptr is an operational no-op, leaving the stolen memory intact under the new owner.
02Why Move Constructors MUST Be Marked 'noexcept'
One of the most critical rules in systems C++ is: Always mark move constructors and move assignment operators noexcept.
Consider what happens when a std::vector<T> grows and must reallocate its internal buffer to double capacity: it allocates a larger array and moves existing elements from the old array to the new array.
If an element's move constructor could potentially throw an exception midway through the reallocation, the vector cannot roll back the move without data loss (the moved-from elements are already modified). To uphold the Strong Exception Guarantee, std::vector checks std::is_nothrow_move_constructible<T>::value.
If the move constructor is NOT marked noexcept, std::vector silently falls back to slow deep copying! Marking move operations noexcept unlocks massive performance gains in standard library containers.
03The Canonical Rule of Five and Rule of Zero
The Rule of Five states: If a class requires a custom implementation of any one of the following special member functions, it almost certainly requires all five to manage its resources correctly:
1. Destructor (~T())
2. Copy Constructor (T(const T&))
3. Copy Assignment Operator (operator=(const T&))
4. Move Constructor (T(T&&) noexcept)
5. Move Assignment Operator (operator=(T&&) noexcept)
Conversely, the modern Rule of Zero states: Classes that do not directly manage raw resources should declare NONE of the special member functions. Instead, rely on modern RAII members (std::string, std::vector, std::unique_ptr) whose compiler-generated special members are automatically correct.
04Rule of Five Summary Reference
The five special member functions and their roles in modern C++ memory management:
Adhering to the Rule of Five or Rule of Zero completely eliminates memory leaks, double-free vulnerabilities, and unintended deep copies.
+-----------------------+-------------------------+-----------------------+| Special Member | Syntax Signature | Primary Action |+-----------------------+-------------------------+-----------------------+| Destructor | ~T() noexcept | Release heap/handles || Copy Constructor | T(const T&) | Allocate fresh deep cp|| Copy Assignment | T& operator=(const T&) | Copy-and-Swap / assign|| Move Constructor | T(T&&) noexcept | Steal ptrs, null source| Move Assignment | T& operator=(T&&) noex | Delete old, steal new |+-----------------------+-------------------------+-----------------------+The ideas worth keeping.
- Move semantics transfers ownership of internal heap pointers in O(1) time via rvalue references.
- Always mark move constructors and move assignment operators noexcept to enable vector optimization.
- Follow the Rule of Five for manual resource managers and the Rule of Zero for high-level composite types.
Make it yours.
Type and test your code right here in the interactive workspace. Run your solution, check the output, and compare with the worked guide when ready.
Exercise 1: What does std move x actually do in compile
What does std::move(x) actually do in compiled machine code? Does it copy or move data by itself?
Explore a worked solution
// std::move does NOT move or copy any data at runtime; it generates ZERO machine instructions.// It is purely a compile-time static_cast to an rvalue reference: static_cast<std::remove_reference_t<T>&&>(x).// It informs the compiler that 'x' can be treated as an expiring rvalue,// enabling overload resolution to select the move constructor or move assignment operator.std::move is an unconditional cast to an rvalue reference.
Exercise 2: Explain what happens if a move constructor st
Explain what happens if a move constructor steals a pointer other.data_ but forgets to set other.data_ = nullptr;.
Explore a worked solution
// If other.data_ is not set to nullptr, both this->data_ and other.data_ point to the same heap block.// When 'other' goes out of scope and its destructor executes, it will delete the memory!// The newly constructed object now holds a dangling pointer. When it later destructs,// a fatal double-free crash occurs.Think about what the destructor of 'other' does when 'other' goes out of scope.
What stayed with you?
Check the ideas while they’re fresh. Your answers stay in this page and feedback appears as you go.
Exercise 1: What does std move x actually do in compile
What does std::move(x) actually do in compiled machine code? Does it copy or move data by itself?
Build completed. Binary executed. [Exit code: 0]
Keep exploring.
The official references behind this lesson, when you want a second angle or the full details.
One lesson closer.
Reading is a start. Trying it yourself makes the connection.