[2026] C++ Exception Specifications | 예외 명세 가이드

[2026] C++ Exception Specifications | 예외 명세 가이드

이 글의 핵심

C++ Exception Specifications: 예외 명세 가이드. 예외 명세 역사·noexcept 기본.

들어가며

예외 명세는 함수가 던질 수 있는 예외를 지정하는 기능입니다.

실무에서 마주한 현실

개발을 배울 때는 모든 게 깔끔하고 이론적입니다. 하지만 실무는 다릅니다. 레거시 코드와 씨름하고, 급한 일정에 쫓기고, 예상치 못한 버그와 마주합니다. 이 글에서 다루는 내용도 처음엔 이론으로 배웠지만, 실제 프로젝트에 적용하면서 “아, 이래서 이렇게 설계하는구나” 하고 깨달은 것들입니다. 특히 기억에 남는 건 첫 프로젝트에서 겪은 시행착오입니다. 책에서 배운 대로 했는데 왜 안 되는지 몰라 며칠을 헤맸죠. 결국 선배 개발자의 코드 리뷰를 통해 문제를 발견했고, 그 과정에서 많은 걸 배웠습니다. 이 글에서는 이론뿐 아니라 실전에서 마주칠 수 있는 함정들과 해결 방법을 함께 다루겠습니다.

1. 예외 명세 역사

C++03 - throw()

아래 코드는 cpp를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// 예외 없음
void func() throw();
// 특정 예외만
void func() throw(std::exception);
void func() throw(int, double);
// C++17에서 제거됨

C++11 - noexcept

아래 코드는 cpp를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// 예외 없음
void func() noexcept;
// 조건부
void func() noexcept(true);   // noexcept
void func() noexcept(false);  // 예외 가능

2. noexcept 기본

기본 사용

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

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

noexcept 검사

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

#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. 이동 연산과 noexcept

이동 생성자

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

#include <vector>
#include <iostream>
class Widget {
public:
    Widget() = default;
    
    // 이동 생성자 (noexcept 권장)
    Widget(Widget&& other) noexcept
        : data(std::move(other.data)) {
        std::cout << "이동 생성" << std::endl;
    }
    
    // 복사 생성자
    Widget(const Widget& other)
        : data(other.data) {
        std::cout << "복사 생성" << 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));  // 이동 생성 (noexcept)
    
    return 0;
}

중요: std::vector는 이동 생성자가 noexcept일 때만 이동을 사용합니다.

4. swap과 noexcept

swap 구현

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

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. 조건부 noexcept

템플릿과 noexcept

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

template<typename T>
class Container {
public:
    // T의 이동 생성자가 noexcept면 noexcept
    Container(Container&& other) 
        noexcept(std::is_nothrow_move_constructible_v<T>)
        : data(std::move(other.data)) {}
    
    // T의 소멸자가 noexcept면 noexcept
    void clear() noexcept(std::is_nothrow_destructible_v<T>) {
        data.clear();
    }
    
private:
    std::vector<T> data;
};

6. 소멸자와 noexcept

암시적 noexcept

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

class MyClass {
public:
    // 소멸자는 암시적으로 noexcept
    ~MyClass() {
        // 예외 던지면 std::terminate
    }
};
class Explicit {
public:
    // 명시적 noexcept
    ~Explicit() noexcept {
        // 예외 던지면 std::terminate
    }
};

소멸자에서 예외 처리

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

class SafeClass {
public:
    ~SafeClass() noexcept {
        try {
            cleanup();
        } catch (const std::exception& e) {
            std::cerr << "정리 중 에러: " << e.what() << std::endl;
        }
    }
    
private:
    void cleanup() {
        // 예외 가능
    }
};

7. 실전 예제

예제 1: 안전한 리소스 관리

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

#include <iostream>
#include <memory>
class Resource {
public:
    Resource() {
        std::cout << "리소스 할당" << std::endl;
    }
    
    ~Resource() noexcept {
        std::cout << "리소스 해제" << std::endl;
    }
    
    void use() noexcept {
        std::cout << "리소스 사용" << 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;
}

정리

핵심 요약

  1. noexcept: 예외 없음 명세 (C++11)
  2. throw(): deprecated (C++17 제거)
  3. 이동 연산: noexcept 권장
  4. 소멸자: 암시적 noexcept
  5. 조건부: noexcept(expression)

noexcept 사용 권장

함수noexcept이유
이동 생성자성능 최적화
이동 대입성능 최적화
swap예외 안전성
소멸자암시적 noexcept
간단한 getter예외 없음
복잡한 로직예외 가능

다음 단계


관련 글

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