█
LastWrite
  • > Curriculum
  • > Pricing
  • > For Educators
  • > About
  • > Contact
Log InGet Started

Questions, concerns, bug reports, or suggestions? We read every message, write to us at [email protected].

More ways to reach us →
LastWrite

Structured computer science lessons for aspiring developers and security professionals.

[email protected]

(201) 785-7951

Mon–Fri, 9 AM–5 PM EST

Learn

  • Curriculum
  • Pricing

Company

  • About
  • For Educators & Schools
  • Contact Us

Legal

  • Terms of Service
  • Privacy Policy
© 2026 LastWrite. All rights reserved.
Curriculum/Programming Languages/C++/Move Semantics
45 minAdvanced

Move Semantics

After this lesson, you will be able to: Use rvalue references, move constructors, std::move, and understand when the compiler elides copies.

Move semantics is the C++11 feature that made modern C++ fast. It's why std::vector<std::string> doesn't copy strings when you reallocate.

Prerequisites:Templates

The problem move semantics solves

Old C++: returning a vector by value copied every element. New C++: moves the internal pointer + size + capacity from old to new object. O(1) instead of O(n). Same for std::string, std::unique_ptr, anything that owns a heap resource.

Rvalue references + move

Two reference types: `T&` for lvalues (named things), `T&&` for rvalues (temporaries).

tsx
#include <string>
#include <utility>
void take(std::string s) { /* by value */ }
void takeLRef(std::string& s) { /* lvalue ref */ }
void takeRRef(std::string&& s) { /* rvalue ref */ }
std::string a = "hello";
take(a); // copies a into s
take(std::string("x")); // temporary, could move-construct s (compiler chooses)
// std::move CASTS to rvalue ref. Doesn't actually move; it tells the next
// operation 'you're allowed to steal from this'.
take(std::move(a)); // moves a into s. a is now in 'moved-from' state.
// a is still a valid object but its contents are unspecified, don't use without reassigning.

Move constructor + move assignment

Define them when you wrap a resource. Otherwise let the compiler synthesize.

sql
class Buffer {
int* data_;
size_t size_;
public:
Buffer(size_t n) : data_(new int[n]), size_(n) {}
~Buffer() { delete[] data_; }
// Move constructor, steal the pointer; null out source
Buffer(Buffer&& other) noexcept
: data_(other.data_), size_(other.size_) {
other.data_ = nullptr;
other.size_ = 0;
}
// Move assignment
Buffer& operator=(Buffer&& other) noexcept {
if (this != &other) {
delete[] data_;
data_ = other.data_;
size_ = other.size_;
other.data_ = nullptr;
other.size_ = 0;
}
return *this;
}
// Disable copy for non-copyable resource
Buffer(const Buffer&) = delete;
Buffer& operator=(const Buffer&) = delete;
};

When the compiler elides copies

Copy elision: compiler skips copy/move entirely in certain return-value contexts. RVO (Return Value Optimization): `return Buffer(100);` constructs directly in caller's storage. NRVO (Named RVO): `Buffer b; ...; return b;` similarly. C++17 mandates copy elision in many contexts. Result: you write returning-by-value code; compiler makes it fast.

💡 noexcept matters

If your move constructor isn't `noexcept`, std::vector falls back to copying when it reallocates (because it can't risk a half-moved state if move throws). Mark every move constructor + move assignment `noexcept`. It's the difference between O(n) and O(1) on vector growth.

Common mistakes

Using a moved-from object (UB-adjacent; technically valid but unspecified state). Forgetting `noexcept` on move operations (silent perf loss). `std::move`-ing a return value (defeats RVO). Just `return x;`. Move-ing const objects (becomes a copy silently).

Sign in and purchase access to unlock this lesson.

Sign in to purchase
←Templates and C++20 Concepts
Back to C++
Modern C++ (C++17/20)→