Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

🧪 PART 2: The Deeper Arcana of Move Semantics

💫 1. std::moveThe Blessing of Transfer

Despite the name, std::move does not move anything.

What it really does is cast a spell that tells the compiler:

“Hey, I solemnly swear I won't use this object anymore. You can steal its soul now.”

So instead of:

T a = b; // Copy

You go:

T a = std::move(b); // Move. b is now a shell of its former self.

🔮 Without std::move, your move constructor never even gets called. Because C++ won’t move something unless you explicitly say "I’m done with it."


🦴 2. std::unique_ptr<T[]>The Sacred RAII Relic

You're tired of writing this, aren’t you?

a = new T[n];
delete[] a;

It’s giving ✨trauma✨.

Instead, you do:

#include <memory>

std::unique_ptr<T[]> a;

a = std::make_unique<T[]>(n);
  • Automatically deletes the memory when the object goes out of scope.
  • You can still move it, but you can’t copy—because it’s unique, like your trauma and your playlist.

Move constructor becomes dead simple:

my_container(my_container&& other) noexcept = default;
my_container& operator=(my_container&& other) noexcept = default;

Let the STL do the heavy lifting while you sip iced coffee like a grown-up.


🧼 3. std::exchangeThe Elegant Memory Swap Spell

Instead of this clunky mess:

a = b.a;
b.a = nullptr;

You say:

a = std::exchange(b.a, nullptr);

Which means:

"Set a to b.a, and reset b.a to nullptr, all in one sexy move."

Perfect for move constructors and move assignment.


🧝‍♀️ 4. Pro-Level Move Assignment: The Real Grownup Version

my_container& operator=(my_container&& other) noexcept {
    if (this != &other) {
        delete[] a;
        a = std::exchange(other.a, nullptr);
    }
    return *this;
}
  • Safe
  • Clean
  • Self-assignment-proof
  • Doesn’t leak
  • Doesn’t double-free

This is C++ that smells like sandalwood, not burnt malloc.


🏁 What Your Glow-Up Class Might Look Like

template <typename T, int n>
class my_container {
public:
    my_container() : a(std::make_unique<T[]>(n)) {}

    explicit my_container(const T* b) : my_container() {
        std::copy(b, b + n, a.get());
    }

    my_container(const my_container& other) : my_container() {
        std::copy(other.a.get(), other.a.get() + n, a.get());
    }

    my_container& operator=(const my_container& other) {
        if (this != &other) {
            std::copy(other.a.get(), other.a.get() + n, a.get());
        }
        return *this;
    }

    // Move semantics: default is enough when using unique_ptr
    my_container(my_container&&) noexcept = default;
    my_container& operator=(my_container&&) noexcept = default;

    void swap(my_container& other) noexcept {
        std::swap(a, other.a);
    }

private:
    std::unique_ptr<T[]> a;
};

Just look at her. She doesn’t even need a destructor anymore. She’s efficient. She’s safe. She’s modern.


👑 TL;DR – The Mystic Moves

ConceptIncantationWhat It Does
std::move(obj)"Take this, I’m done with it."Enables move constructor/assign
std::unique_ptr"I will own this alone."RAII for heap memory
std::exchange()"Take this, and replace it with that."Swaps a value and returns old
= default"I trust the STL gods to handle this."Autogenerate move/copy properly

You want me to walk you through adapting your current class with unique_ptr line by line next? Or maybe go further into move-only types and real STL container internals? Just say the magic word, sorceress 🧙‍♀️✨