본문으로 건너뛰기
Previous
Next
C++ Exception Specifications: noexcept, History, and throw

C++ Exception Specifications: noexcept, History, and throw

C++ Exception Specifications: noexcept, History, and throw

이 글의 핵심

C++ exception specifications from throw() to noexcept: move operations, swap, destructors, conditional noexcept, and why dynamic specs were removed.

Introduction

Exception specifications describe what exceptions a function may throw.

1. History of exception specifications

C++03 — throw()

// No exceptions
void func() throw();
// Specific types
void func() throw(std::exception);
void func() throw(int, double);
// Removed in C++17

C++11 — noexcept

// No exceptions
void func() noexcept;
// Conditional
void func() noexcept(true);   // noexcept
void func() noexcept(false);  // may throw

2. noexcept basics

Basic usage

#include <iostream>
void safe_func() noexcept {
    std::cout << "no throw" << std::endl;
}
void risky_func() {
    throw std::runtime_error("error");
}
int main() {
    safe_func();
    
    try {
        risky_func();
    } catch (const std::exception& e) {
        std::cout << e.what() << std::endl;
    }
    
    return 0;
}

Checking noexcept

#include <iostream>
#include <type_traits>
void func1() noexcept {}
void func2() {}
int main() {
    std::cout << std::boolalpha;
    std::cout << "func1: " << noexcept(func1()) << std::endl;  // true
    std::cout << "func2: " << noexcept(func2()) << std::endl;  // false
    
    return 0;
}

3. Move operations and noexcept

Move constructor

#include <vector>
#include <iostream>
class Widget {
public:
    Widget() = default;
    
    Widget(Widget&& other) noexcept
        : data(std::move(other.data)) {
        std::cout << "move ctor" << std::endl;
    }
    
    Widget(const Widget& other)
        : data(other.data) {
        std::cout << "copy ctor" << std::endl;
    }
    
private:
    std::vector<int> data{1, 2, 3};
};
int main() {
    std::vector<Widget> vec;
    vec.reserve(10);
    
    Widget w;
    vec.push_back(std::move(w));  // move ctor (noexcept)
    
    return 0;
}

Important: std::vector often requires noexcept move constructors to move elements during reallocation.

4. swap and noexcept

swap implementation

class Data {
public:
    Data(int v) : value(v) {}
    
    void swap(Data& other) noexcept {
        std::swap(value, other.value);
    }
    
    int getValue() const noexcept { return value; }
    
private:
    int value;
};
namespace std {
    template<>
    void swap(Data& lhs, Data& rhs) noexcept {
        lhs.swap(rhs);
    }
}
int main() {
    Data d1(10), d2(20);
    std::swap(d1, d2);
    
    std::cout << d1.getValue() << std::endl;  // 20
    std::cout << d2.getValue() << std::endl;  // 10
    
    return 0;
}

5. Conditional noexcept

Templates and noexcept

template<typename T>
class Container {
public:
    Container(Container&& other) 
        noexcept(std::is_nothrow_move_constructible_v<T>)
        : data(std::move(other.data)) {}
    
    void clear() noexcept(std::is_nothrow_destructible_v<T>) {
        data.clear();
    }
    
private:
    std::vector<T> data;
};

6. Destructors and noexcept

Implicit noexcept

class MyClass {
public:
    // destructor implicitly noexcept unless a member’s dtor can throw
    ~MyClass() {
        // throwing here -> std::terminate during stack unwind
    }
};
class Explicit {
public:
    ~Explicit() noexcept {
        // still must not throw
    }
};

Exceptions in destructor cleanup

class SafeClass {
public:
    ~SafeClass() noexcept {
        try {
            cleanup();
        } catch (const std::exception& e) {
            std::cerr << "cleanup error: " << e.what() << std::endl;
        }
    }
    
private:
    void cleanup() {
        // may throw
    }
};

7. Practical example: resource management

#include <iostream>
#include <memory>
class Resource {
public:
    Resource() {
        std::cout << "allocate" << std::endl;
    }
    
    ~Resource() noexcept {
        std::cout << "release" << std::endl;
    }
    
    void use() noexcept {
        std::cout << "use" << std::endl;
    }
};
class Manager {
public:
    Manager() : res(std::make_unique<Resource>()) {}
    
    Manager(Manager&& other) noexcept
        : res(std::move(other.res)) {}
    
    Manager& operator=(Manager&& other) noexcept {
        res = std::move(other.res);
        return *this;
    }
    
    void process() noexcept {
        if (res) {
            res->use();
        }
    }
    
private:
    std::unique_ptr<Resource> res;
};
int main() {
    Manager m1;
    m1.process();
    
    Manager m2 = std::move(m1);
    m2.process();
    
    return 0;
}

Summary

Key points

  1. noexcept: C++11 exception contract
  2. throw(): deprecated (removed in C++17)
  3. Move operations: mark noexcept when safe
  4. Destructors: implicitly noexcept in normal cases
  5. Conditional: noexcept(expression)

When to use noexcept

Functionnoexcept?Reason
Move ctorYesContainer performance
Move assignYesContainer performance
swapYesException safety
DestructorYes (default)Avoid terminate during unwind
Simple gettersOftenNo throws
Complex logicMaybe notMay throw

Next steps



자주 묻는 질문 (FAQ)

Q. 이 내용을 실무에서 언제 쓰나요?

A. C++ exception specifications from throw() to noexcept: move operations, swap, destructors, conditional noexcept, and why… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.

Q. 선행으로 읽으면 좋은 글은?

A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.

Q. 더 깊이 공부하려면?

A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.


같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.


이 글에서 다루는 키워드 (관련 검색어)

C++, Exception Specifications, noexcept, throw, Deprecated 등으로 검색하시면 이 글이 도움이 됩니다.