[2026] C++ =default and =delete: Special Members, ODR, and Rule of Zero

[2026] C++ =default and =delete: Special Members, ODR, and Rule of Zero

이 글의 핵심

C++11 =default and =delete: controlling special members, non-copyable types, deleted conversions, heap-only types, Rule of Five vs Rule of Zero, and noexcept moves.

What are =default and =delete?

They let you explicitly control special member functions. 아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.

class MyClass {
public:
    MyClass() = default;              // request default ctor
    MyClass(const MyClass&) = delete; // forbid copy ctor
};

=default

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Point {
private:
    int x, y;
    
public:
    Point() = default;
    
    Point(const Point&) = default;
    Point& operator=(const Point&) = default;
    
    Point(Point&&) = default;
    Point& operator=(Point&&) = default;
    
    ~Point() = default;
};

=delete

아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class NonCopyable {
public:
    NonCopyable() = default;
    
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable& operator=(const NonCopyable&) = delete;
    
    NonCopyable(NonCopyable&&) = default;
    NonCopyable& operator=(NonCopyable&&) = default;
};

Practical examples

Example 1: Singleton (illustrative)

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Singleton {
private:
    static Singleton* instance;
    
    Singleton() = default;
    
public:
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    Singleton(Singleton&&) = delete;
    Singleton& operator=(Singleton&&) = delete;
    
    static Singleton* getInstance() {
        if (!instance) {
            instance = new Singleton();
        }
        return instance;
    }
};
Singleton* Singleton::instance = nullptr;

Example 2: Resource handle

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class FileHandle {
private:
    FILE* file;
    
public:
    explicit FileHandle(const char* filename) {
        file = fopen(filename, "r");
    }
    
    ~FileHandle() {
        if (file) {
            fclose(file);
        }
    }
    
    FileHandle(const FileHandle&) = delete;
    FileHandle& operator=(const FileHandle&) = delete;
    
    FileHandle(FileHandle&& other) noexcept : file(other.file) {
        other.file = nullptr;
    }
    
    FileHandle& operator=(FileHandle&& other) noexcept {
        if (this != &other) {
            if (file) {
                fclose(file);
            }
            file = other.file;
            other.file = nullptr;
        }
        return *this;
    }
};

Example 3: Banning a conversion

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class SafeInt {
private:
    int value;
    
public:
    SafeInt(int v) : value(v) {}
    
    SafeInt(double) = delete;  // block double -> SafeInt accidents
    
    int getValue() const {
        return value;
    }
};
int main() {
    SafeInt x(10);      // OK
    // SafeInt y(3.14); // error: deleted ctor
}

Example 4: No heap allocation

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <cstddef>
class StackOnly {
public:
    StackOnly() = default;
    void* operator new(std::size_t) = delete;
    void* operator new[](std::size_t) = delete;
    void operator delete(void*) = delete;
    void operator delete[](void*) = delete;
};
int main() {
    StackOnly obj;  // OK: automatic storage
    // auto* p = new StackOnly();  // error
}

Note: std::vector<StackOnly> and similar may still be constrained because allocators can require constructible storage—document intent.

Rule of Five

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Resource {
private:
    int* data;
    
public:
    Resource(int value) : data(new int(value)) {}
    
    ~Resource() {
        delete data;
    }
    
    Resource(const Resource& other) 
        : data(new int(*other.data)) {}
    
    Resource& operator=(const Resource& other) {
        if (this != &other) {
            delete data;
            data = new int(*other.data);
        }
        return *this;
    }
    
    Resource(Resource&& other) noexcept 
        : data(other.data) {
        other.data = nullptr;
    }
    
    Resource& operator=(Resource&& other) noexcept {
        if (this != &other) {
            delete data;
            data = other.data;
            other.data = nullptr;
        }
        return *this;
    }
};

Rule of Zero

아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.

class Resource {
private:
    std::unique_ptr<int> data;
    
public:
    Resource(int value) : data(std::make_unique<int>(value)) {}
};

Common pitfalls

Pitfall 1: Delete copy, move disappears

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class MyClass {
public:
    MyClass(const MyClass&) = delete;
    MyClass& operator=(const MyClass&) = delete;
    // move may be implicitly deleted
};
class MyClass {
public:
    MyClass(const MyClass&) = delete;
    MyClass& operator=(const MyClass&) = delete;
    
    MyClass(MyClass&&) = default;
    MyClass& operator=(MyClass&&) = default;
};

Pitfall 2: =default with uninitialized members

아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class MyClass {
    int x;  // indeterminate if not initialized
    
public:
    MyClass() = default;
};
class MyClass {
    int x = 0;
    
public:
    MyClass() = default;
};

Pitfall 3: Non-virtual destructor in hierarchy

아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.

class Base {
public:
    ~Base() = default;  // not virtual
};
class Base {
public:
    virtual ~Base() = default;
};

Overload control

아래 코드는 cpp를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

void func(int x) {
    std::cout << "int: " << x << std::endl;
}
void func(double x) = delete;
int main() {
    func(10);      // OK
    // func(3.14); // error
}

Deleted template specialization

아래 코드는 cpp를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

template<typename T>
void process(T value) {
    std::cout << "generic" << std::endl;
}
template<>
void process<double>(double) = delete;
int main() {
    process(10);      // OK
    // process(3.14); // error
}

default vs explicit bodies

아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class PointA {
    int x = 0, y = 0;
public:
    PointA() = default;
};
class PointB {
    int x, y;
public:
    PointB() : x(0), y(0) {}
};

Deep dive: =default with noexcept and triviality

If move operations are not noexcept, containers may copy on reallocation. When moves cannot throw, prefer noexcept with = default. 아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// 타입 정의
struct Buffer {
    Buffer(Buffer&&) noexcept = default;
    Buffer& operator=(Buffer&&) noexcept = default;
};

Trivial types optimize well; = default signals “nothing special here” to reviewers.

Deep dive: =default on abstract interfaces

Even interface-like classes can use = default on a virtual destructor: 다음은 간단한 cpp 코드 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.

struct IPlugin {
    virtual ~IPlugin() = default;
    virtual void run() = 0;
};

Deep dive: =delete to block bad conversions

아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.

struct Index {
    explicit Index(int v) : v_(v) {}
    Index(double) = delete;
private:
    int v_{};
};

Deep dive: size and performance

Deleted functions impose no body in the binary—purely compile-time bans. = default often matches or beats hand-written trivial members.

Deep dive: debugging table

SymptomCheck
Copy deleted, move also goneAdd explicit = default for move
Slicing / partial destroyvirtual ~Base() = default
Cannot store in vectorType may be immovable—revisit design

Deep dive: common mistakes

  • Default ctor private + = default with no other ctors—maybe unusable outside class—verify intent.
  • Partially deleted operator=—other assignments may still exist—review Rule of Five.
  • User destructor without considering copy/move—update for Rule of Five/Zero.

Deep dive: thread handle style wrapper

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class UniqueHandle {
    void* h_{nullptr};
public:
    explicit UniqueHandle(void* h) : h_(h) {}
    ~UniqueHandle() { /* close(h_) */ }
    UniqueHandle(const UniqueHandle&) = delete;
    UniqueHandle& operator=(const UniqueHandle&) = delete;
    UniqueHandle(UniqueHandle&& o) noexcept : h_(o.h_) { o.h_ = nullptr; }
    UniqueHandle& operator=(UniqueHandle&& o) noexcept {
        if (this != &o) {
            /* close(h_) */
            h_ = o.h_;
            o.h_ = nullptr;
        }
        return *this;
    }
};

FAQ

Q1: When to use =default?

A:

  • Express “compiler-generated is fine”
  • After defining other ctors you still need defaults
  • Keep types trivial when possible

Q2: When to use =delete?

A:

  • Forbid copy/move
  • Forbid dangerous conversions
  • Forbid heap use (operator new delete)

Q3: Rule of Five vs Zero?

A:

  • Five: you manage resources directly
  • Zero: delegate to smart pointers / containers (preferred when possible)

Q4: Copy deleted—what about move?

A: Often implicitly deleted—re-enable move explicitly if that is the design.

Q5: Benefits of =default?

A:

  • Clear intent
  • Good codegen for trivial types

Q6: Resources?

A:

  • Effective Modern C++
  • cppreference.com
  • C++ Primer

Practical tips

Debugging

  • Warnings first

Performance

  • Profile

Code review

  • Conventions

Practical checklist

Before coding

  • Right approach?
  • Maintainable?
  • Performance?

While coding

  • Warnings?
  • Edge cases?
  • Errors?

At review

  • Intent?
  • Tests?
  • Docs?

Keywords

C++, default, delete, special member functions, C++11

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3