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::stringstd::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.
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
sourceis a named variable, so it is normally an lvalue.std::move(source)converts it to an rvalue.targetcan now use the move constructor ofstd::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";
aowns the characters"cat".ais a named object, so it is an lvalue.
2. Call std::move(a)
std::string b = std::move(a);
std::move(a)castsato an rvalue reference.- This tells the compiler that
acan be moved from. - The move constructor of
std::stringis selected.
3. Construct b
btakes over the internal string data froma.- No full copy is needed.
4. State after move
bcontains"cat".ais 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
ais allowed because the object is valid. - But the output for
amay vary by implementation. bshould printcat.
Key takeaway
The transfer happens because:
std::move(a)marksaas movable.std::stringhas move support.- 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.
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
| Concept | What it does |
|---|---|
std::move(x) | Casts x to an rvalue so moving is allowed |
| Move constructor / move assignment | Actually transfers resources |
| Copy constructor / copy assignment | Duplicates resources |
Copy vs move
| Operation | Source object after operation | Cost | Typical use |
|---|---|---|---|
| Copy | Unchanged | Often higher | When both objects need the same value |
| Move | Valid but unspecified | Often lower | When ownership/value is being transferred |
std::move() vs std::forward()
| Utility | Main purpose | Typical use |
|---|---|---|
std::move() | Always treat an object as movable | When you are done with that object |
std::forward() | Preserve whether an argument was lvalue or rvalue | In 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
| Term | Simple meaning |
|---|---|
| lvalue | A named object or persistent value |
| rvalue | A 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.
Cheat Sheet
Quick reference
Include
#include <utility>
Basic use
std::move(x)
What it does
- Casts
xto 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
constand 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.
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." ]
Keep learning
Related questions
Advantages of Brace Initialization in C++
Learn why C++ brace initialization is often clearer and safer than other object initialization styles, with examples and common pitfalls.
Basic Rules and Idioms for Operator Overloading in C++
Learn the core rules, syntax, and common idioms for operator overloading in C++, including member vs non-member operators.
C++ Aggregates, Trivial Types, Trivially Copyable Types, and PODs Explained
Learn what aggregates, trivial types, trivially copyable types, and PODs mean in C++, how they differ, and why they matter.