Question

Copy-and-Swap Idiom in C++: What It Is, Why It Matters, and When to Use It

cppcopy-constructorassignment-operatorc++-faqcopy-and-swap

Question

In C++, what is the copy-and-swap idiom, and when should it be used? What problems does it solve in class design and assignment operators? Also, did the recommended approach change with C++11 and move semantics?

Related areas of confusion often include:

  • how to implement a copy constructor and copy assignment operator safely
  • whether a shared helper function can be used for copying and assignment
  • how copy elision interacts with this pattern
  • how resource-owning classes behave when managing dynamic memory

Short Answer

By the end of this page, you will understand what the copy-and-swap idiom is in C++, why it is used to implement assignment safely, and how it helps with exception safety and self-assignment. You will also see how C++11 move semantics changed the way this idiom is used in modern C++ code.

Concept

The copy-and-swap idiom is a common C++ technique for implementing the assignment operator safely and cleanly.

At a high level, it works like this:

  1. Make a copy of the right-hand side object.
  2. Swap the current object's data with the copy.
  3. Let the temporary copy go out of scope and clean up the old data.

A typical form looks like this:

class Buffer {
public:
    Buffer& operator=(Buffer other) {
        swap(*this, other);
        return *this;
    }

    friend void swap(Buffer& a, Buffer& b) noexcept {
        using std::swap;
        swap(a.size, b.size);
        swap(a.data, b.data);
    }

private:
    std::size_t size{};
    int* data{};
};

The parameter other is passed by value, which means it is copied (or moved) before the function body runs. Once the copy succeeds, the function swaps the current object with that local copy.

What problem does it solve?

The idiom mainly solves three issues:

1. Exception safety

If copying the right-hand side fails, the current object is not modified.

That gives you the strong exception guarantee:

  • either the assignment succeeds completely
  • or the object stays unchanged

2. Self-assignment safety

With copy-and-swap, obj = obj; works correctly without a special self-assignment check in most cases.

3. Code simplicity

Instead of manually deleting old resources, allocating new ones, and carefully handling every failure path, you centralize the risky work in the copy constructor and the cleanup in the destructor.

Why this matters in real C++

C++ often deals with resource management:

  • dynamic memory
  • file handles
  • sockets
  • mutex wrappers
  • custom heap buffers

Whenever a class owns a resource, assignment becomes tricky. A bad assignment operator can cause:

  • memory leaks
  • double deletion
  • partially updated objects
  • invalid state after exceptions

Copy-and-swap is one way to write safer resource-owning classes.

The big idea

Instead of trying to modify the existing object carefully in place, you first build a safe replacement, then swap it in.

That is often simpler and safer than updating piece by piece.

What changed in C++11?

C++11 introduced move semantics, which changed the picture.

Before C++11, copy-and-swap was a very popular default strategy for writing assignment operators.

After C++11:

  • passing by value may use a move instead of a copy for rvalues
  • many classes can rely on the Rule of Zero by using standard library types like std::vector, std::string, and smart pointers
  • sometimes a custom copy-and-swap assignment operator is no longer the best performance choice

So the idiom is still valid, but it is not always the first tool you should reach for in modern C++.

If your class manually manages a resource, copy-and-swap can still be useful. But if your class can be built from RAII standard types, the best solution is often to let the compiler generate special member functions or use defaults.

Mental Model

Think of your object like a person carrying a backpack.

  • The current object is wearing an old backpack.
  • The right-hand side object represents a new backpack setup.
  • Instead of emptying the old backpack item by item and risking mistakes, you first prepare a complete new backpack.
  • Then you swap backpacks.
  • The temporary person walks away with the old backpack, and when they leave, the old backpack is destroyed safely.

This model explains why the idiom is safe:

  • if preparing the new backpack fails, you keep the old one unchanged
  • if it succeeds, the swap is quick and simple
  • cleanup happens automatically when the temporary object is destroyed

So copy-and-swap is really: build the replacement first, then trade places.

Take Quiz

Syntax and Examples

Core syntax

A classic copy-and-swap assignment operator looks like this:

class MyClass {
public:
    MyClass(const MyClass& other);  // copy constructor
    ~MyClass();

    MyClass& operator=(MyClass other) {
        swap(*this, other);
        return *this;
    }

    friend void swap(MyClass& a, MyClass& b) noexcept {
        using std::swap;
        swap(a.value, b.value);
    }

private:
    int* value{};
};

Beginner-friendly example

Here is a small class that owns a dynamic array:

#include <algorithm>
#include <cstddef>
#include <iostream>

class IntArray {
public:
    IntArray(std::size_t size = 0)
        : size_(size), data_(size ? new int[size] : nullptr) {}

    IntArray(const IntArray& other)
        : size_(other.size_), data_(other.size_ ? new int[other.size_] : nullptr) {
        std::copy(other.data_, other.data_ + size_, data_);
    }

    ~IntArray() {
        delete[] data_;
    }

    IntArray& operator=(IntArray other) {
        swap(*this, other);
        return *this;
    }

    friend void swap(IntArray& a, IntArray& b) noexcept {
        using std::swap;
        swap(a.size_, b.size_);
        swap(a.data_, b.data_);
    }

    void set(std::size_t index, int value) {
        data_[index] = value;
    }

    int get(std::size_t index) const {
        return data_[index];
    }

    std::size_t size() const {
        return size_;
    }

private:
    std::size_t size_{};
    int* data_{};
};

Why this works

When you do:

IntArray a(3);
IntArray b(5);
a = b;

these steps happen:

  1. b is copied into the by-value parameter other
  2. a swaps its internal data with other
  3. other now holds a's old data
  4. when other is destroyed, the old data is deleted

Why swap should be noexcept

The idiom works best when swap cannot throw exceptions. If swapping itself throws, the strong exception guarantee becomes harder to maintain.

That is why custom swap functions are usually written as noexcept when possible.

C++11 version with moves

In C++11 and later, the same by-value operator can benefit from move construction for temporaries:

IntArray a;
a = IntArray(10); // may move into the parameter instead of copying

That means the idiom became more flexible, although sometimes a dedicated move assignment operator is still more efficient.

Step by Step Execution

Consider this code:

IntArray a(2);
a.set(0, 10);
a.set(1, 20);

IntArray b(2);
b.set(0, 1);
b.set(1, 2);

a = b;

Let us trace a = b;.

Before assignment

  • a owns memory containing: [10, 20]
  • b owns memory containing: [1, 2]

Step 1: Call assignment operator

The compiler calls:

a.operator=(b)

Because the parameter is passed by value, b is copied into a local object named other.

Now:

  • a owns: [10, 20]
  • b owns: [1, 2]
  • other owns a separate copy: [1, 2]

If this copy fails due to an exception, a is still unchanged.

Step 2: Swap a and other

Inside the operator:

swap(*this, other);

After the swap:

  • a owns: [1, 2]
  • other owns: [10, 20]

Step 3: Return from the function

The function returns *this, which is a.

Step 4: Destroy other

The local variable other goes out of scope. Its destructor runs and deletes the memory it owns.

That means the old data from a is cleaned up automatically.

Final state

  • a now contains a copy of b
  • b is unchanged
  • a's old memory was deleted safely

This is why the idiom is attractive: the dangerous steps are isolated and cleanup is automatic.

Real World Use Cases

Copy-and-swap is most useful in classes that own resources directly.

Common examples

Dynamic memory wrappers

A custom container, matrix, buffer, or string-like class may own raw heap memory.

File or handle wrappers

If a class owns a low-level handle and supports copying by duplicating the handle, safe assignment logic is needed.

Legacy C++ codebases

Older projects often have custom resource-owning classes written before modern standard library usage became common.

Educational implementations

When learning the Rule of Three or Rule of Five, copy-and-swap is a clean way to understand safe assignment.

Example scenarios

  • a Matrix class reallocates memory when assigned a matrix of different size
  • a TextBuffer class stores a manually managed character array
  • a Image class owns pixel data and must avoid leaks during assignment
  • a custom String class must support self-assignment and exception safety

When it is less necessary

If you use standard types like these:

  • std::vector
  • std::string
  • std::unique_ptr
  • std::shared_ptr

then you often do not need copy-and-swap manually, because those types already manage resources correctly.

Take Quiz

Real Codebase Usage

In real projects, developers usually prefer the simplest safe design.

Modern pattern: Rule of Zero first

If your class is composed of well-behaved members, write this:

class UserProfile {
public:
    std::string name;
    std::vector<int> scores;
};

No custom destructor, copy constructor, or assignment operator is needed.

This is usually better than implementing copy-and-swap manually.

When custom assignment still appears

Developers still use custom assignment operators when:

  • wrapping a C library resource
  • maintaining older code with raw pointers
  • implementing low-level containers
  • needing a strong exception guarantee for replacement-style assignment

Common patterns around copy-and-swap

1. Custom swap

A class defines a swap function that swaps all resource-owning members.

friend void swap(Buffer& a, Buffer& b) noexcept {
    using std::swap;
    swap(a.size_, b.size_);
    swap(a.data_, b.data_);
}

2. By-value assignment

One operator handles both copied lvalues and moved rvalues:

Buffer& operator=(Buffer other) {
    swap(*this, other);
    return *this;
}

3. Validation before replacement

A similar idea appears outside assignment too: create a valid replacement first, then commit the change only when ready.

4. Strong exception safety

This pattern is used whenever partial updates would be dangerous.

Performance note

In high-performance code, developers may prefer a dedicated copy assignment operator and dedicated move assignment operator if they can do less work than creating a full by-value copy.

So in real codebases, copy-and-swap is valued mainly for:

  • correctness
  • simplicity
  • exception safety

but it is sometimes avoided when performance is more important and the class can support a more direct move assignment.

Common Mistakes

1. Forgetting to implement a proper swap

If swap does not exchange every important member, the object may become inconsistent.

Broken example:

friend void swap(IntArray& a, IntArray& b) noexcept {
    using std::swap;
    swap(a.data_, b.data_);
    // forgot to swap size_
}

Why it is wrong:

  • the pointer and size no longer match
  • later access may read invalid memory

2. Writing unsafe manual assignment first

Beginners often write assignment like this:

IntArray& operator=(const IntArray& other) {
    delete[] data_;
    size_ = other.size_;
    data_ = new int[size_];
    std::copy(other.data_, other.data_ + size_, data_);
    return *this;
}

Problems:

  • if new throws, the object is already damaged
  • self-assignment can cause trouble because you delete your own data before copying

3. Ignoring self-assignment

This broken pattern can fail on a = a;:

IntArray& operator=(const IntArray& other) {
    delete[] data_;
    data_ = new int[other.size_];
    std::copy(other.data_, other.data_ + other.size_, data_);
    size_ = other.size_;
    return *this;
}

If other is the same object, other.data_ may refer to memory that was just deleted.

4. Letting swap throw

If swapping can throw exceptions, you lose much of the safety benefit.

Try to make swap cheap and noexcept.

5. Using copy-and-swap when Rule of Zero is better

This is not a bug, but it is a design mistake.

Instead of this:

class BadIdea {
    int* data;
    // lots of manual special member functions
};

prefer:

#include <vector>

class BetterIdea {
    std::vector<int> data;
};

If the standard library can manage the resource for you, use it.

6. Assuming copy-and-swap is always fastest

It is often elegant, but not always optimal.

A by-value parameter may create an extra copy in some cases, especially in pre-C++11 code or for expensive objects.

Comparisons

Copy-and-swap vs manual copy assignment

ApproachMain ideaException safetySelf-assignment safetyComplexity
Copy-and-swapCopy first, then swapStrong, if copy and swap are safeUsually automaticSimpler
Manual assignmentRebuild current object in placeEasy to get wrongMust be handled carefullyMore error-prone

Pre-C++11 vs C++11 and later

VersionHow copy-and-swap fits
Pre-C++11Very common way to write safe assignment for resource-owning classes
C++11+Still valid, but move semantics and Rule of Zero reduce how often you need it

By-value assignment vs separate copy/move assignment

StyleProsCons
operator=(T other)Simple, unified implementation, works with copy and moveMay do extra work compared with specialized move assignment
operator=(const T&) plus operator=(T&&)Can be more efficientMore code to write and maintain

Copy-and-swap vs Rule of Zero

TechniqueBest when
Copy-and-swapYou manually manage a resource and need safe assignment
Rule of ZeroYour class uses standard RAII members and needs no custom resource logic

Important takeaway

Copy-and-swap is a tool, not a universal rule. In modern C++, the preferred order is usually:

  1. Rule of Zero
  2. Default special members when appropriate
  3. Custom copy/move logic only when necessary
  4. Copy-and-swap when it gives a clear safety and simplicity benefit
Take Quiz

Cheat Sheet

Copy-and-swap quick reference

Purpose

  • Implement copy assignment safely
  • Handle self-assignment naturally
  • Provide strong exception safety

Core pattern

class T {
public:
    T(const T& other);

    T& operator=(T other) {
        swap(*this, other);
        return *this;
    }

    friend void swap(T& a, T& b) noexcept {
        using std::swap;
        swap(a.member1, b.member1);
        swap(a.member2, b.member2);
    }
};

How it works

  • other is copied or moved into the parameter
  • swap current object with other
  • other destroys the old state when it goes out of scope

Benefits

  • strong exception safety
  • self-assignment safety
  • simpler assignment code

Requirements

  • correct copy constructor
  • correct destructor
  • correct swap
  • swap should ideally be noexcept

Best used when

  • class owns raw resources
  • you need safe replacement-style assignment

Less needed when

  • using std::string, std::vector, smart pointers
  • Rule of Zero can be applied

C++11 note

  • by-value parameter can use move construction for rvalues
  • dedicated move assignment may still be faster in some classes

Watch out for

  • forgetting to swap all members
  • writing throwing swap
  • using manual raw pointers when standard containers would be better

FAQ

What is the copy-and-swap idiom in C++?

It is a way to implement assignment by first copying the source object, then swapping its contents with the current object. The old data is cleaned up automatically when the temporary copy is destroyed.

Why is copy-and-swap considered safe?

Because the current object is not modified until copying succeeds. If copying throws an exception, the object stays unchanged.

Does copy-and-swap handle self-assignment?

Yes, in most normal implementations it does. Since the right-hand side is copied first, obj = obj; does not corrupt the object.

Is copy-and-swap still useful in C++11 and later?

Yes, but it is less universally needed. Move semantics and standard RAII types often make custom assignment operators unnecessary.

Is operator=(T other) better than operator=(const T& other)?

Not always. The by-value form is elegant and safe, but a separate move assignment operator can sometimes be more efficient.

Should I always use copy-and-swap for resource-owning classes?

No. First consider whether the class should own raw resources directly at all. If you can use std::vector, std::string, or smart pointers, that is usually better.

What is the relationship between copy-and-swap and the Rule of Three?

If your class manually manages a resource, you often need a destructor, copy constructor, and copy assignment operator. Copy-and-swap is one way to implement the assignment part safely.

What is the relationship between copy-and-swap and the Rule of Five?

In C++11+, move constructor and move assignment operator may also matter. Copy-and-swap can still be used, but it is now part of a larger design decision around copy and move behavior.

Take Quiz

Mini Project

Description

Build a small C++ class named TextBuffer that stores dynamically allocated character data and uses the copy-and-swap idiom for assignment. This project demonstrates safe resource management, self-assignment safety, and clean cleanup logic.

Goal

Create a resource-owning class that supports copying and assignment safely using copy-and-swap.

Requirements

  1. Create a TextBuffer class that owns a dynamically allocated character array.
  2. Implement a constructor, copy constructor, destructor, and assignment operator.
  3. Implement a swap function that exchanges all important members.
  4. Demonstrate copying, assignment, and self-assignment in main().
  5. Print the stored text to show that assignment works correctly.
Take Quiz

Keep learning

Related questions

View more