Move Semantics in C++: What It Is and Why a Beginner Needs It

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

What's Behind the Clever Term "Move Semantics"?

\n\n

Imagine you're moving to a new apartment. You have a huge, heavy sofa. What's faster: dragging it yourself down five flights of stairs, or simply handing over the keys to the movers and saying, "Guys, the sofa is yours, do whatever you want with it, I don't need it anymore"? The second option is essentially move semantics in C++.

\n\n

Before the C++11 standard, programmers painfully copied data. Every time you returned a vector or a string from a function, the compiler created an exact copy. If the vector contained a million elements, it honestly copied each one. This was slow, inefficient, and resembled rewriting a book by hand instead of just handing over the original.

\n\n

Move semantics is a mechanism that allows you to move resources (memory, file descriptors, sockets) from one object to another without expensive copying. After the move, the source object remains in an "empty" but valid state, and it can be safely destroyed or assigned a new value.

\n\n

In this article, we'll break down rvalue references, the std::move function, move constructors, and move assignment operators — in simple terms with code examples.

\n\n

Why Does Code Slow Down Without Move Semantics?

\n\n

Let's look at a classic example that plagued all C++ programmers before 2011:

\n\n
#include <iostream>\n#include <vector>\n#include <string>\n\nstd::vector<std::string> createBigVector() {\n    std::vector<std::string> vec(1000000, "Hello");\n    return vec;  // Horror! Copying a million strings!\n}\n\nint main() {\n    std::vector<std::string> data = createBigVector();\n    std::cout << "Size: " << data.size() << std::endl;\n    return 0;\n}
\n\n

What's happening here? The function createBigVector creates a vector, fills it with data, and returns it. The compiler (without optimizations) does the following:

\n
    \n
  1. Creates the vector inside the function.
  2. \n
  3. Copies the ENTIRE vector into the return value.
  4. \n
  5. Destroys the original vector.
  6. \n
  7. In main, copies that value into the variable data.
  8. \n
\n\n

Result: two full copies of a million strings. This isn't just slow — it's a performance disaster.

\n\n

Rvalue and Lvalue: Two Worlds of C++

\n\n

To understand move semantics, you need to grasp two fundamental concepts: lvalue and rvalue.

\n\n
    \n
  • Lvalue (left value) — an object that has an address in memory and a name. You can take its address (&obj). Example: the variable int x = 5; — x is an lvalue.
  • \n
  • Rvalue (right value) — a temporary value that has no name and cannot appear on the left side of an assignment operator. Example: 5, x + 3, the result of a function getTemp(). You cannot write &(x + 3).
  • \n
\n\n

Previously, rvalue references (denoted by &&) did not exist. The compiler saw a temporary object and said, "Aha, this is an rvalue, the only thing I can do with it is copy it or destroy it." Move semantics gave the compiler a third option: steal resources from the rvalue, because it's going to die soon anyway.

\n\n

How Does std::move and Rvalue References Work?

\n\n

The function std::move is simply a type cast. It doesn't physically move data; it just turns an lvalue into an rvalue, giving the compiler permission to "plunder" the object.

\n\n

Let's look at a simple class that manages dynamic memory:

\n\n
#include <iostream>\n#include <cstring>\n\nclass MyString {\nprivate:\n    cha                

Blogs

Book Recommendations