Module 25: Move Semantics in C++
Introduction
Move Semantics is one of the most powerful features introduced in C++11. It allows objects to transfer ownership of resources instead of copying them, making programs significantly faster and more memory-efficient.
Before C++11, objects were copied whenever they were passed or returned. Copying large objects could be expensive because all their data had to be duplicated.
Move semantics solves this problem by transferring resources instead of duplicating them.
Learning Objectives
After completing this module, you will understand:
- What Move Semantics is
- Difference between Copy and Move
- Lvalue and Rvalue
- Move Constructor
- Move Assignment Operator
- std::move()
- Perfect Forwarding
- std::forward()
- Performance benefits
- Best practices
Why Move Semantics?
Suppose you have a large vector.
1std::vector<int> numbers(1000000);
If you copy it,
1std::vector<int> copy = numbers;
Memory allocated:
Original Vector
1,000,000 integers
↓
Copy
Another 1,000,000 integers
Huge memory usage.
Instead,
1std::vector<int> moved = std::move(numbers);
No copying occurs.
Ownership is transferred.
Copy vs Move
| Copy | Move |
|---|---|
| Creates duplicate data | Transfers ownership |
| Slow | Fast |
| More memory | No extra memory |
| Original object unchanged | Original object becomes empty/valid |
What is an Lvalue?
An Lvalue is an object that has a name and a memory address.
Example
1int x = 10;
x
↓
Memory Address
Examples
1int a = 5; 2 3std::string name = "Tech3Space"; 4 5int arr[5];
All are lvalues.
Lvalue Example
1#include <iostream> 2using namespace std; 3 4int main() 5{ 6 int x = 100; 7 8 int& ref = x; 9 10 cout << ref; 11}
Output
100
What is an Rvalue?
An Rvalue is a temporary value that does not have a persistent memory location.
Example
15 2 310 + 20 4 5std::string("Hello")
These objects exist only temporarily.
Example
1int x = 10 + 20;
10 + 20
↓
Temporary
↓
Assigned to x
↓
Destroyed
Lvalue vs Rvalue
| Lvalue | Rvalue |
|---|---|
| Has name | Temporary |
| Has memory address | Temporary object |
| Can appear on left side | Usually right side only |
| Can take address | Usually cannot |
Example
1int x = 10; 2 3x = 20;
x is Lvalue.
20
is an Rvalue.
Rvalue References
C++11 introduced
1&&
Example
1int&& value = 50; 2 3std::cout << value;
Output
50
Why Rvalue References?
They allow moving resources instead of copying them.
Temporary Object
↓
Move Resources
↓
Destroy Temporary
Copy Constructor Review
1class Student 2{ 3public: 4 5 Student(const Student&) 6 { 7 cout << "Copy Constructor"; 8 } 9};
Called when copying an object.
Move Constructor
Syntax
1ClassName(ClassName&& other);
Example
1#include <iostream> 2#include <utility> 3 4using namespace std; 5 6class Buffer 7{ 8private: 9 10 int* data; 11 12public: 13 14 Buffer() 15 { 16 data = new int(100); 17 18 cout << "Constructor\n"; 19 } 20 21 Buffer(const Buffer& other) 22 { 23 data = new int(*other.data); 24 25 cout << "Copy Constructor\n"; 26 } 27 28 Buffer(Buffer&& other) noexcept 29 { 30 data = other.data; 31 32 other.data = nullptr; 33 34 cout << "Move Constructor\n"; 35 } 36 37 ~Buffer() 38 { 39 delete data; 40 41 cout << "Destructor\n"; 42 } 43}; 44 45int main() 46{ 47 Buffer b1; 48 49 Buffer b2 = std::move(b1); 50}
Output
Constructor
Move Constructor
Destructor
Destructor
How Move Constructor Works
b1
↓
Memory
↓
Move
↓
b2 owns Memory
↓
b1 = nullptr
No copying occurs.
Why Set nullptr?
1other.data = nullptr;
Without it,
Both objects
↓
Delete Same Memory
↓
Crash
Always leave moved-from objects in a valid state.
std::move()
std::move() converts an lvalue into an rvalue reference.
Syntax
1std::move(object)
Example
1std::string name = "Tech3Space"; 2 3std::string newName = std::move(name);
After moving
name
↓
Empty (Valid)
↓
newName
↓
Owns Data
Example
1#include <iostream> 2#include <string> 3 4using namespace std; 5 6int main() 7{ 8 string s1 = "Hello"; 9 10 string s2 = std::move(s1); 11 12 cout << "s1 = " << s1 << endl; 13 14 cout << "s2 = " << s2 << endl; 15}
Possible Output
s1 =
s2 = Hello
A moved-from string is valid but its exact content is unspecified.
Move Assignment Operator
Syntax
1ClassName& operator=(ClassName&& other);
Example
1#include <iostream> 2 3using namespace std; 4 5class Buffer 6{ 7 int* data; 8 9public: 10 11 Buffer() 12 { 13 data = new int(100); 14 } 15 16 Buffer& operator=(Buffer&& other) noexcept 17 { 18 if(this != &other) 19 { 20 delete data; 21 22 data = other.data; 23 24 other.data = nullptr; 25 } 26 27 cout << "Move Assignment\n"; 28 29 return *this; 30 } 31 32 ~Buffer() 33 { 34 delete data; 35 } 36}; 37 38int main() 39{ 40 Buffer a; 41 42 Buffer b; 43 44 b = std::move(a); 45}
Output
Move Assignment
Copy Assignment vs Move Assignment
| Copy Assignment | Move Assignment |
|---|---|
| Duplicates resources | Transfers resources |
| Slower | Faster |
| Allocates memory | Usually no allocation |
Returning Objects
Without move semantics
Function
↓
Copy Object
↓
Return
With move semantics
Function
↓
Move Object
↓
Return
Modern compilers may also apply Return Value Optimisation (RVO) or copy elision, avoiding both copy and move in many cases.
Perfect Forwarding
Sometimes we want to forward arguments while preserving whether they are lvalues or rvalues.
This is called Perfect Forwarding.
std::forward()
Syntax
1std::forward<T>(value)
It preserves the original value category.
Example
1#include <iostream> 2#include <utility> 3 4using namespace std; 5 6void display(int& value) 7{ 8 cout << "Lvalue\n"; 9} 10 11void display(int&& value) 12{ 13 cout << "Rvalue\n"; 14} 15 16template<typename T> 17void forwardValue(T&& value) 18{ 19 display(std::forward<T>(value)); 20} 21 22int main() 23{ 24 int x = 10; 25 26 forwardValue(x); 27 28 forwardValue(20); 29}
Output
Lvalue
Rvalue
Why Perfect Forwarding?
Without forwarding
Argument
↓
Becomes Lvalue
↓
Wrong Function Called
With forwarding
Argument
↓
std::forward()
↓
Original Type Preserved
Real-World Example
Imagine moving houses.
Copy
Old House
↓
Duplicate Every Item
↓
Two Houses Full
Slow and expensive.
Move
Old House
↓
Move Furniture
↓
New House
↓
Old House Empty
Fast and efficient.
Performance Comparison
| Operation | Copy | Move |
|---|---|---|
| Memory Allocation | High | Low |
| Speed | Slower | Faster |
| CPU Usage | Higher | Lower |
| Large Objects | Expensive | Efficient |
Best Practices
✅ Use std::move() only when you no longer need the original object.
1std::string b = std::move(a);
✅ Prefer move constructors for resource-owning classes.
✅ Always set moved-from pointers to nullptr.
✅ Mark move operations noexcept when possible.
✅ Use std::forward() in forwarding templates.
Common Mistakes
Forgetting std::move()
1std::string a = "Hello"; 2 3std::string b = a;
Copies the string.
Correct
1std::string b = std::move(a);
Using Moved Object Incorrectly
1std::string s = "ABC"; 2 3auto t = std::move(s); 4 5std::cout << s;
A moved-from object is valid, but its value should not be relied upon.
Forgetting Self-Assignment Check
1if(this != &other)
Always include this in assignment operators.
Interview Questions
1. What is Move Semantics?
A C++11 feature that transfers ownership of resources instead of copying them.
2. What is std::move()?
It casts an object to an rvalue reference, enabling move operations if available.
3. Difference between Lvalue and Rvalue?
An lvalue has a persistent identity and memory location, while an rvalue is typically a temporary value.
4. Why is Move Constructor faster?
It transfers ownership instead of allocating and copying resources.
5. What is Perfect Forwarding?
A technique using forwarding references and std::forward() to preserve whether an argument is an lvalue or rvalue.
6. Why use noexcept in move constructors?
Many standard library containers use move operations only when they are guaranteed not to throw exceptions.
7. When should std::forward() be used?
Inside function templates that forward arguments to another function while preserving their value category.
Module Summary
In this module, you learned:
- What Move Semantics is
- Difference between Copy and Move
- Lvalue and Rvalue
- Rvalue References (
&&) - Move Constructor
- Move Assignment Operator
std::move()- Perfect Forwarding
std::forward()- Performance improvements and best practices
You now have the knowledge to write modern, efficient C++ code that minimises unnecessary copying and takes full advantage of move semantics introduced in C++11.