C++: Move Semantics Deep Dive

Last updated: 2026-08-26

In Lesson 44, we finished learning about multi-thread synchronization.

Now, we'll dive into one of the most important features of C++11 — move semantics.

Understanding move semantics is key to truly understanding modern C++ performance optimization.


1. Move Semantics Overview

(1) 1.1 Why Do We Need Move Semantics?

Problem: In C++98/03, copying temporary objects (rvalues) is wasteful.

Example: Copying Temporary Objects (Difficulty ⭐)

▶ Example 2: STL Container Usage (Difficulty ⭐)

TEXT 📖 Display only
std::vector<int> createVector() {
 std::vector<int> v = {1, 2, 3, 4, 5};
 return v; // Return temporary object
}

std::vector<int> dest = createVector(); // Copy the temporary object, wasteful!

Output:

TEXT 📖 Display only
Copy count: 0

Move semantics solution: "Steal" the temporary object's resources instead of copying them.


(2) 1.2 Lvalue vs Rvalue

Category Description Example
Lvalue Has a name, can take address x in int x = 10;
Rvalue Temporary object, about to be destroyed 10, x + 1, function return values

Real-life analogy:



2. Rvalue References

(1) 2.1 Basic Syntax

Rvalue references use && and can only bind to rvalues.

Example: Rvalue References (Difficulty ⭐)

CPP
#include <iostream>

void process(int& x) {
 std::cout << "Processing lvalue: " << x << std::endl;
}

void process(int&& x) {
 std::cout << "Processing rvalue: " << x << std::endl;
}

int main() {
 int a = 10;
 process(a); // Calls process(int&)
 process(20); // Calls process(int&&)
 
 return 0;
}

(2) 2.2 std::move

std::move is used to convert an lvalue to an rvalue reference, signaling "I no longer need this object, you can steal its resources."

Example: Using std::move to Transfer Resources (Difficulty ⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <utility>

int main() {
 std::vector<int> v1 = {1, 2, 3};
 std::vector<int> v2 = std::move(v1); // Move construction: v1's resources are "stolen" by v2
 
 std::cout << "v2 size: " << v2.size() << std::endl; // 3
 std::cout << "v1 size: " << v1.size() << std::endl; // 0 (v1 is empty after being moved from)
 
 return 0;
}

💡 Tip:



3. Move Constructor and Move Assignment

(1) 3.1 Why Do We Need Custom Move Operations?

Problem: Compiler-generated move operations may not be efficient (e.g., for deep-copy classes).

Solution: Define custom move constructor and move assignment operator.


(2) 3.2 Example: Implementing Move Operations (Difficulty ⭐⭐⭐)

CPP
#include <iostream>
#include <cstring>

class String {
private:
 char* data;
 size_t length;

public:
 // Constructor
 String(const char* str) {
 length = strlen(str);
 data = new char[length + 1];
 strcpy(data, str);
 std::cout << "Construct:" << data << std::endl;
 }
 
 // Copy constructor (deep copy)
 String(const String& other) {
 length = other.length;
 data = new char[length + 1];
 strcpy(data, other.data);
 std::cout << "Copy construction: " << data << std::endl;
 }
 
 // Move constructor (steal resources)
 String(String&& other) noexcept {
 data = other.data; // Steal the pointer
 length = other.length;
 other.data = nullptr; // Set to null, prevent double free
 other.length = 0;
 std::cout << "Move construction" << std::endl;
 }
 
 // Destructor
 ~String() {
 delete data;
 }
};

int main() {
 String s1("Hello");
 String s2 = std::move(s1); // Calls the move constructor
 
 return 0;
}

Output:

TEXT 📖 Display only
Construct:Hello
Move construction

Execution Result:

TEXT 📖 Display only
Construct:Hello
Move construction


4. Rules of Move Semantics

(1) 4.1 The Big Five

If a class needs a custom destructor, copy constructor, or copy assignment operator, it typically also needs custom move constructor and move assignment operator.

Function Description
Destructor Release resources
Copy constructor Deep copy
Copy assignment operator Deep copy assignment
Move constructor Steal resources
Move assignment operator Steal resources assignment

(2) 4.2 Rule of Zero

Best Practice: If you use smart pointers to manage resources, you don't need to define any of the Big Five (the compiler will automatically generate correct versions).

TEXT 📖 Display only
class Person {
 std::string name; // Using string, automatically supports move
 std::vector<int> scores; // Using vector, automatically supports move
 // No need to define the Big Five!
}


5. Perfect Forwarding

(1) 5.1 What Is Perfect Forwarding?

Perfect forwarding means passing arguments to other functions without altering their lvalue/rvalue nature.

Example: Using std::forward for Perfect Forwarding (Difficulty ⭐⭐⭐)

CPP
#include <iostream>
#include <utility>

void process(int& x) {
 std::cout << "Processing lvalue" << std::endl;
}

void process(int&& x) {
 std::cout << "Processing rvalue" << std::endl;
}

template<typename T>
void wrapper(T&& arg) {
 process(std::forward<T>(arg)); // Perfect forwarding
}

int main() {
 int x = 10;
 wrapper(x); // Forwards as lvalue
 wrapper(20); // Forwards as rvalue
 
 return 0;
}


6. Performance Advantages of Move Semantics

▶ Example 1: Comparing Copy and Move (Difficulty ⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <chrono>

int main() {
 std::vector<std::vector<int>> v;
 
 // Test copy
 auto start = std::chrono::high_resolution_clock::now();
 for (int i = 0; i < 10000; i++) {
 std::vector<int> temp(1000, 1);
 v.push_back(temp); // Copy
 }
 auto end = std::chrono::high_resolution_clock::now();
 auto copy_time = std::chrono::duration<double>(end - start).count();
 
 v.clear();
 
 // Test move
 start = std::chrono::high_resolution_clock::now();
 for (int i = 0; i < 10000; i++) {
 std::vector<int> temp(1000, 1);
 v.push_back(std::move(temp)); // Move
 }
 end = std::chrono::high_resolution_clock::now();
 auto move_time = std::chrono::duration<double>(end - start).count();
 
 std::cout << "Copy time: " << copy_time << " seconds" << std::endl;
 std::cout << "Move time: " << move_time << " seconds" << std::endl;
 
 return 0;
}
▶ Try it Yourself

Output:

TEXT 📖 Display only
Copy time: 0.5 seconds
Move time: 0.01 seconds

▶ Example 3: std::move Transfers Resource Ownership (Difficulty ⭐)

CPP
#include <iostream>
#include <vector>
#include <string>

int main() {
    std::string original = "Hello, C++!";
    std::string moved = std::move(original);

    std::cout << "moved: " << moved << std::endl;
    std::cout << "original after move: \"" << original << "\"" << std::endl;

    return 0;
}
▶ Try it Yourself

Output:

TEXT 📖 Display only
moved: Hello, C++!
original after move: ""

❓ FAQ

Q Does std::move always perform a move?
A Not necessarily. If there's no move constructor, the copy constructor will be called instead (fallback).

Q: What state is an object in after being moved from? A: A moved-from object is in a valid but unspecified state (typically empty). You can assign a new value to it, but you cannot assume its value.


Q When should I use move semantics?
A - Temporary objects (function return values) - Objects no longer needed (std::move) - Container element insertion (emplace_back)

📖 Summary

Concept Key Point
Rvalue reference &&, can only bind to rvalues
std::move Converts an lvalue to an rvalue reference
Move constructor Steals resources, no copy
Move assignment Steals resources for assignment
Perfect forwarding std::forward, preserves value category

📝 Exercises

  1. Basic (Difficulty ⭐): Create a vector<string>, add several strings using push_back, and observe the output to confirm copies are happening. Then compare with emplace_back.

  2. Intermediate (Difficulty ⭐⭐): Implement a MyString class (with both copy constructor and move constructor), trigger move semantics via std::move in main, and observe which constructor is called.

  3. Challenge (Difficulty ⭐⭐⭐): Implement a move-only type (like unique_ptr), disable copy constructor and copy assignment, enable move constructor and move assignment. Write test code to verify it cannot be copied but can be moved.



Next Lesson: Smart Pointers Advanced (#46)

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏