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로 컴파일해보면 경고나 에러가 발생하지 않고 있다.