The Six Special Member Functions
C++ automatically generates up to six **special member functions** if you don't declare them: **default constructor**, **copy constructor**, **copy assignment**, **destructor**, **move constructor**, and **move assignment** (last two since C++11). Understanding exactly **when the compiler generates each one** — and when it suppresses generation — is essential for writing correct resource-managing classes. The key insight: declaring ANY of the copy/move/destructor suppresses automatic generation of others.
Compiler-Generated Specials — The Complete Rules
#include <iostream>
#include <string>
// ── What the compiler generates and when ─────────────────────
//
// DEFAULT CONSTRUCTOR — generated unless any other constructor is declared
//
// DESTRUCTOR — always generated (unless you declare one)
//
// COPY CONSTRUCTOR — generated unless:
// - you declare a copy constructor
// - you declare a move constructor (then copy is DELETED)
// - you declare a move assignment (then copy is DELETED)
//
// COPY ASSIGNMENT — generated unless:
// - you declare a copy assignment operator
// - you declare a move constructor (then copy-assign is DELETED)
// - you declare a move assignment (then copy-assign is DELETED)
//
// MOVE CONSTRUCTOR — generated ONLY if:
// - no user-declared copy constructor
// - no user-declared copy assignment
// - no user-declared move constructor
// - no user-declared move assignment
// - no user-declared destructor
//
// MOVE ASSIGNMENT — generated ONLY if: (same conditions as move ctor)
struct Fully_Generated {
int x;
// All 6 auto-generated (default bodies — shallow copy)
};
struct No_Default {
int x;
No_Default(int v) : x(v) {} // declared → default suppressed
// copy ctor, copy assign: still generated
// move ctor, move assign: still generated
// No_Default nd; ← compile error: no default constructor
};
struct Move_Disabled {
int x;
~Move_Disabled() {} // destructor declared → move SUPPRESSED
// copy still generated (deprecated legacy behaviour — avoid this pattern!)
// move ctor: NOT generated
// move assign: NOT generated
// This class copies even when you std::move it!
};
// ── Checking generated functions ──────────────────────────────
void demonstrate_generation() {
static_assert(std::is_default_constructible_v<Fully_Generated>);
static_assert(std::is_copy_constructible_v<Fully_Generated>);
static_assert(std::is_move_constructible_v<Fully_Generated>);
static_assert(!std::is_default_constructible_v<No_Default>);
static_assert(std::is_copy_constructible_v<No_Default>);
static_assert(std::is_move_constructible_v<No_Default>);
// Move_Disabled has deprecated auto-copy but no auto-move
static_assert(!std::is_move_constructible_v<Move_Disabled>); // copies instead
std::cout << "All static assertions passed
";
}
int main() {
demonstrate_generation();
return 0;
}Common Mistakes
- Declaring a destructor suppresses move — if you add a destructor to a class that should be movable (e.g., for logging), the move constructor and move assignment are NOT generated. You must explicitly `= default` them or write them.
- Relying on deprecated auto-generated copy when destructor declared — the auto-generated copy constructor/assignment when a user destructor is declared is deprecated as of C++11. Always explicitly write or `= default` your copy operations when you have a custom destructor.
- Forgetting that declaring a move suppresses copy — `MyClass(MyClass&&) = default;` suppresses the auto-generated copy constructor. Callers get a compile error when trying to copy. Either declare `MyClass(const MyClass&) = default;` explicitly or use the Rule of 5 completely.
Tip
Tip
Practice The Six Special Member Functions in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
The four pillars of Object-Oriented Programming in C++
Practice Task
Note
Practice Task — (1) Write a working example of The Six Special Member Functions from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with The Six Special Member Functions is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready cpp code.
Key Takeaways
- C++ automatically generates up to six **special member functions** if you don't declare them: **default constructor**, **copy constructor**, **copy assignment**, **destructor**, **move constructor**, and **move assignment** (last two since C++11).
- Declaring a destructor suppresses move — if you add a destructor to a class that should be movable (e.g., for logging), the move constructor and move assignment are NOT generated. You must explicitly `= default` them or write them.
- Relying on deprecated auto-generated copy when destructor declared — the auto-generated copy constructor/assignment when a user destructor is declared is deprecated as of C++11. Always explicitly write or `= default` your copy operations when you have a custom destructor.
- Forgetting that declaring a move suppresses copy — `MyClass(MyClass&&) = default;` suppresses the auto-generated copy constructor. Callers get a compile error when trying to copy. Either declare `MyClass(const MyClass&) = default;` explicitly or use the Rule of 5 completely.