Software Workshop

C++11부터는 함수 정의에 어떤 예외를 던질 지의 사양(specification)을 기술할 수 있다.

throw 구문과 try ... catch 구문이 제대로 짝을 이뤄야 예외 처리가 가능하므로 다소 까다로운 편이다.

첫번째 예제 - 예외 사양 없는 케이스

우선, 예외 사양과 상관없는 아주 간단한 예외 처리 예제를 살펴본다.

#include <iostream>
#include <stdexcept>

using namespace std;

void my_func() {
    throw out_of_range("out of range!");
}

int main(void)
{
    try {
        my_func();
    } catch (exception& e) {
        cout << "caught in handler" << endl;
    }

    return 0;
}

일부러 예외를 발생시키고 그걸 catch하는 예제이다.

이 예제를 C++11, C++14, C++17 중 어떤 환경에서 실행해도 동일하게 동작하는 것을 확인할 수 있다.

caught in handler

두번째 예제 - 예외 사양와 종류를 명시한 케이스

#include <iostream>
#include <stdexcept>

using namespace std;

void my_func() throw(out_of_range) {
    throw out_of_range("out of range!");
}

int main(void)
{
    try {
        my_func();
    } catch (exception& e) {
        cout << "caught in handler" << endl;
    }

    return 0;
}

my_func()이 out_of_range 예외를 던질 거라는 예외 사양을 선언한 부분이 달라졌다.

환경마다 동작 방식이 조금씩 달라진다.

C++98

컴파일 타임 경고없이 실행된다.

caught in handler

C++11, C++14

컴파일 타임에 다음과 같은 경고가 발생하지만, 실행 결과는 동일하다.
동적 예외 사양이 표준에서 제외되었음을 경고하고 있다.

exception1.cc:6:16: warning: dynamic exception specifications are deprecated in C++11 [-Wdeprecated]
    6 | void my_func() throw(out_of_range) {
      |                ^~~~~

그래도 실행은 잘 된다.

caught in handler

C++17

C++17에서는 완전히 표준에서 제외되어 경고가 아닌 컴파일 타임 에러가 발생한다

exception1.cc:6:16: error: ISO C++17 does not allow dynamic exception specifications
    6 | void my_func() throw(out_of_range) {
      |                ^~~~~

컴파일 타임 에러에 해당하므로 실행되지 않았다.

세번째 예제 - 예외 사양을 명시하되 예외 종류는 명시하지 않은 케이스

#include <iostream>
#include <stdexcept>

using namespace std;

void my_func() throw() {
    throw out_of_range("out of range!");
}

int main(void)
{
    try {
        my_func();
    } catch (exception& e) {
        cout << "caught in handler" << endl;
    }

    return 0;
}

throw()로 선언된 것에 주목할 것

예외 종류가 명시되지 않았기 때문에 try ... catch 구문이 있음에도 불구하고 catch를 하지 못하고 core dump가 발생한다. 환경과 상관없이 동일하다.

terminate called after throwing an instance of 'std::out_of_range'
  what():  out of range!
[1]    2349965 abort (core dumped)  ./exception1

결론

안타깝게도 C++17에서는 dynamic exception specification이 금지되었기 때문에, 첫번째 예제와 같이 throw 구문없이 함수를 정의하는 방식으로 코드를 작성해야 한다.

예외 사양 타입

C++17에서 예외 사용도 C++ 언어의 타입 시스템에 포함되도록 변경되었다.

void f() noexcept(true)와 void f() noexcept(false)가 서로 다른 타입을 가진다고 보는 것이다.

void my_func1() noexcept(true) {
    throw out_of_range("out of range in my_func1!");
}

void (&my_func)() noexcept(false) = my_func1;

그러나 막상 GCC로 컴파일해보면 경고나 에러가 발생하지 않고 있다.