Question

What Is std::move() in C++ and When Should You Use It?

cppc++11move-semanticsc++-faqstdmove

Question

In C++, what is std::move()?

What does it actually do, and in what situations should it be used?

A clear explanation with practical examples would be helpful, especially for understanding how it relates to move semantics and object ownership.

Short Answer

By the end of this page, you will understand that std::move() does not move anything by itself. Instead, it converts an object into an rvalue so that move constructors or move assignment operators may be used. You will also learn when using std::move() is appropriate, when it is unnecessary or harmful, and how it appears in real C++ code.

Concept

std::move() is a standard library utility in C++ that enables move semantics.

At a high level, move semantics let a program transfer ownership of expensive resources—such as heap memory, file handles, or buffers—from one object to another without making a full copy.

What std::move() really does

std::move() does not physically move data.

It simply casts its argument to an rvalue reference type, which signals:

  • "This object may be treated as a temporary."
  • "Its resources can be taken from, if the type supports moving."

A simplified idea looks like this:

template <class T>
constexpr typename std::remove_reference<T>::type&& move(T&& t) noexcept;

That means std::move(x) says: treat x as something that can be moved from.

Why this matters

Before C++11, objects were mostly copied. Copying can be expensive for types like:

  • std::string
  • std::vector
  • smart pointers such as std::unique_ptr
  • user-defined types that own resources

Moving is often faster because the destination can take over the source object's internal resource instead of duplicating it.

Important rule

After moving from an object, it is still valid, but its value is usually unspecified.

That means:

  • you may destroy it
  • you may assign a new value to it
  • you should not rely on its previous contents

For example:

std::string a = "hello";
std::string b = std::move(a);

After this, b now owns the string data. a is still valid, but you should not assume it still contains "hello".

std::move() only enables moving

Actual moving happens only if the receiving operation supports it, such as:

  • a move constructor
  • a move assignment operator
  • a function overload taking T&&

If move support does not exist, the code may fall back to copying instead.

Mental Model

Think of an object as a person holding a backpack.

  • Copying means making a brand-new backpack with the same contents.
  • Moving means handing the existing backpack to someone else.
  • std::move() is not the handoff itself.
  • It is more like saying: "I am done needing this backpack in its current state; you may take it."

After that handoff, the original person is still there, but you should not expect them to still have the backpack contents.

This model helps explain why std::move() is just permission to move, not the move itself.

Take Quiz

Syntax and Examples

Basic syntax

std::move(object)

Include the header:

#include <utility>

Example: moving a string

#include <iostream>
#include <string>
#include <utility>

int main() {
    std::string source = "Hello, world!";
    std::string target = std::move(source);

    std::cout << "target: " << target << '\n';
    std::cout << "source: " << source << '\n';
}

What this means

  • source is a named variable, so it is normally an lvalue.
  • std::move(source) converts it to an rvalue.
  • target can now use the move constructor of std::string.

Example: moving into a vector

#include <iostream>
#include <string>
#include <vector>
#include <utility>

int main() {
    std::vector<std::string> words;
    std::string s = "example";

    words.push_back(std::move(s));

    std::cout << "words[0]: " << words[0] << '\n';
}

Here, push_back can move the string into the vector instead of copying it.

Example: move-only types

Some types cannot be copied at all, but they can be moved.

#include <memory>
#include <utility>

int main() {
    std::unique_ptr<int> p1 = std::make_unique<int>(42);
    std::unique_ptr<int> p2 = std::move(p1);
}

This transfers ownership from p1 to p2. Copying a std::unique_ptr is not allowed.

Example: using std::move in a constructor

#include <string>
#include <utility>

class Person {
public:
    Person(std::string name) : name_(std::move(name)) {}

private:
    std::string name_;
};

This is a common pattern:

  • the parameter is received by value
  • the member takes ownership with std::move(name)

It works well because the parameter is a local copy inside the constructor.

Step by Step Execution

Consider this example:

#include <iostream>
#include <string>
#include <utility>

int main() {
    std::string a = "cat";
    std::string b = std::move(a);

    std::cout << "a: " << a << '\n';
    std::cout << "b: " << b << '\n';
}

Step-by-step

1. Create a

std::string a = "cat";
  • a owns the characters "cat".
  • a is a named object, so it is an lvalue.

2. Call std::move(a)

std::string b = std::move(a);
  • std::move(a) casts a to an rvalue reference.
  • This tells the compiler that a can be moved from.
  • The move constructor of std::string is selected.

3. Construct b

  • b takes over the internal string data from a.
  • No full copy is needed.

4. State after move

  • b contains "cat".
  • a is still valid.
  • But a's exact content is not something your code should depend on.

5. Output

std::cout << "a: " << a << '\n';
std::cout << "b: " << b << '\n';
  • Printing a is allowed because the object is valid.
  • But the output for a may vary by implementation.
  • b should print cat.

Key takeaway

The transfer happens because:

  1. std::move(a) marks a as movable.
  2. std::string has move support.
  3. The move constructor is chosen.

Real World Use Cases

Returning large objects efficiently

Modern C++ often returns objects by value. Compilers frequently optimize this well, but move semantics also help when ownership is transferred.

std::vector<int> build_data() {
    std::vector<int> data = {1, 2, 3, 4};
    return data;
}

In many cases, you do not need to write std::move(data) here because return value optimization may apply.

Transferring ownership of resources

std::unique_ptr depends on move semantics.

std::unique_ptr<File> open_file();

A unique pointer cannot be copied, so moving is how ownership is passed safely.

Inserting expensive objects into containers

std::vector<std::string> logs;
std::string message = "Request completed";
logs.push_back(std::move(message));

This avoids an unnecessary copy when message is no longer needed.

Implementing resource-owning classes

Classes that manage memory, sockets, mutex wrappers, or file descriptors often define move operations so resources can be transferred safely.

Reusing buffers in performance-sensitive code

Applications such as parsers, network servers, and data-processing systems often move strings, vectors, and custom resource wrappers to reduce allocations and copying.

Take Quiz

Real Codebase Usage

In real projects, std::move() is commonly used in a few predictable patterns.

1. Moving from local parameters into members

class Config {
public:
    Config(std::string path) : path_(std::move(path)) {}
private:
    std::string path_;
};

This is common because the parameter is a local object that is about to go out of scope.

2. Transferring ownership with smart pointers

void set_logger(std::unique_ptr<Logger> logger) {
    logger_ = std::move(logger);
}

This makes ownership transfer explicit.

3. Container insertion

items.push_back(std::move(item));

Used when item will not be needed in its old state afterwards.

4. Move constructors and move assignment operators

class Buffer {
public:
    Buffer(Buffer&& other) noexcept
        : data_(other.data_), size_(other.size_) {
        other.data_ = nullptr;
        other.size_ = 0;
    }

    Buffer& operator=(Buffer&& other) noexcept {
        if (this != &other) {
            delete[] data_;
            data_ = other.data_;
            size_ = other.size_;
            other.data_ = nullptr;
            other.size_ = 0;
        }
        return *this;
    }

private:
    int* data_ = nullptr;
    std::size_t size_ = 0;
};

Inside these functions, developers manually transfer resources.

5. Guarding against accidental copies

When a type is expensive to copy, developers may design APIs so callers can move into them or pass by value and then move internally.

6. Error handling and ownership boundaries

In larger systems, move semantics make ownership changes clear:

  • a function creates a resource
  • another object takes ownership
  • the original object no longer owns it

That clarity helps prevent leaks and double-deletion bugs.

Common Mistakes

1. Thinking std::move() always moves

It only casts to an rvalue. If the type has no move constructor or move assignment operator, a copy may still happen.

MyType a;
MyType b = std::move(a); // may copy if MyType is not movable

2. Using the moved-from object as if nothing happened

Broken idea:

std::string name = "Alice";
std::string other = std::move(name);

if (name == "Alice") {
    // Wrong assumption
}

Avoid this by only:

  • destroying the moved-from object
  • assigning a new value to it
  • using operations that are guaranteed safe for its valid-but-unspecified state

3. Moving from an object too early

std::string s = "hello";
process(std::move(s));
std::cout << s; // maybe valid, but old content should not be assumed

Only move when you are truly done with the original value.

4. Using std::move() on const objects

const std::string s = "hello";
std::string t = std::move(s);

This usually does not move usefully, because moving typically modifies the source, and a const object cannot be modified. So this often falls back to copying.

5. Returning std::move(local) unnecessarily

std::string make_name() {
    std::string s = "Bob";
    return std::move(s); // often unnecessary
}

This can interfere with return value optimization. Usually write:

std::string make_name() {
    std::string s = "Bob";
    return s;
}

or simply:

std::string make_name() {
    return "Bob";
}

6. Moving from the same object more than once

std::string s = "data";
use(std::move(s));
use(std::move(s)); // dangerous design unless you reset s first

After the first move, s is still valid but its useful content is not guaranteed.

7. Forgetting that named rvalue references are lvalues

void f(std::string&& s) {
    std::string x = s;            // copy, because s is a named variable
    std::string y = std::move(s); // move
}

Even though s has type std::string&&, the name s itself is an lvalue expression.

Comparisons

std::move() vs actual moving

ConceptWhat it does
std::move(x)Casts x to an rvalue so moving is allowed
Move constructor / move assignmentActually transfers resources
Copy constructor / copy assignmentDuplicates resources

Copy vs move

OperationSource object after operationCostTypical use
CopyUnchangedOften higherWhen both objects need the same value
MoveValid but unspecifiedOften lowerWhen ownership/value is being transferred

std::move() vs std::forward()

UtilityMain purposeTypical use
std::move()Always treat an object as movableWhen you are done with that object
std::forward()Preserve whether an argument was lvalue or rvalueIn forwarding/reference-wrapper templates

For beginners, the main idea is:

  • use std::move() when you want to explicitly allow moving
  • use std::forward() mainly in generic template code

Lvalue vs rvalue

TermSimple meaning
lvalueA named object or persistent value
rvalueA temporary value or something safe to move from

Named variables are usually lvalues, even if their type is T&&. That is why std::move() is often needed.

Take Quiz

Cheat Sheet

Quick reference

Include

#include <utility>

Basic use

std::move(x)

What it does

  • Casts x to an rvalue reference
  • Does not move by itself
  • Enables move constructor / move assignment selection

Use it when

  • you are done with an object's current value
  • you want to transfer ownership
  • you are moving into a container or member
  • you are working with move-only types like std::unique_ptr

Do not use it when

  • you still need the original value unchanged
  • you are returning a local variable by value in simple cases
  • the source object is const and you expect a real move

Safe assumptions after move

  • object is valid
  • object can be destroyed
  • object can be assigned a new value
  • old content should not be relied on

Common pattern

class User {
public:
    User(std::string name) : name_(std::move(name)) {}
private:
    std::string name_;
};

Move-only example

std::unique_ptr<int> a = std::make_unique<int>(5);
std::unique_ptr<int> b = std::move(a);

Important edge case

const std::string s = "hi";
std::string t = std::move(s); // usually copy, not useful move

FAQ

What is std::move() in simple terms?

It tells C++ that an object may be treated as temporary, so its resources can be transferred instead of copied.

Does std::move() actually move data?

No. It only casts an object to an rvalue reference. The actual move happens only if a move operation is available.

When should I use std::move()?

Use it when you no longer need an object's current value and want to transfer its contents or ownership to another object.

Is it safe to use an object after std::move()?

Yes, but only carefully. The object remains valid, but its value is unspecified. You should not assume it still contains the old data.

Why doesn't std::move() work well with const objects?

Because moving usually modifies the source object. A const object cannot be modified, so move operations often cannot be used and copying happens instead.

Should I use std::move() when returning a local variable?

Usually no. Returning the local variable directly often allows better optimization and is simpler.

Why do I need std::move() for std::unique_ptr?

Because std::unique_ptr cannot be copied. Ownership must be transferred with move semantics.

What is the difference between std::move() and std::forward()?

std::move() always treats something as movable. std::forward() preserves whether an argument was originally an lvalue or rvalue, mainly in template code.

Take Quiz

Mini Project

Description

Create a small C++ program that models transferring ownership of a resource using move semantics. This project demonstrates when std::move() is useful and what happens to the source object after the transfer. It also shows a realistic case where copying should be disabled but moving should be allowed.

Goal

Build a move-only FileHandle-style class and transfer ownership from one object to another using std::move().

Requirements

[ "Create a class that owns a resource represented by a string or integer handle.", "Disable copying so the resource cannot be duplicated accidentally.", "Implement a move constructor and move assignment operator.", "Move one object into another using std::move().", "Print the state before and after the move to show ownership transfer." ]

Take Quiz

Keep learning

Related questions

View more