Namespaces
Variants

Dynamic exception specification (until C++17)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous
Exceptions
try block
Throwing exceptions
Handling exceptions
Exception specification
noexcept specification (C++11)
dynamic specification ( until C++17* )
noexcept operator (C++11)

함수가 직접 또는 간접적으로 발생시킬 수 있는 예외를 나열합니다.

목차

구문

throw( type-id-list  (선택 사항) ) (1) (C++11에서 사용 중단됨)
(C++17에서 제거됨)
1) 명시적 동적 예외 사양.
type-id-list - 쉼표로 구분된 type-ids 목록 , 팩 확장을 나타내는 type-id 뒤에는 생략 부호(...)가 옴 (C++11부터)

명시적 동적 예외 사양은 선언 또는 정의의 최상위 유형인 함수 타입, 함수에 대한 포인터 타입, 함수에 대한 참조 타입, 또는 멤버 함수에 대한 포인터 타입의 함수 선언자에만 나타나야 하며, 또는 함수 선언자에서 매개변수나 반환 타입으로 나타나는 그러한 타입에만 나타나야 합니다.

void f() throw(int);            // OK: 함수 선언
void (*pf)() throw (int);       // OK: 함수 포인터 선언
void g(void pfa() throw(int));  // OK: 함수 포인터 매개변수 선언
typedef int (*pf)() throw(int); // 오류: typedef 선언

설명

함수가 동적 예외 사양에 나열된 T 타입으로 선언된 경우, 해당 함수는 그 타입이나 그로부터 파생된 타입의 예외를 던질 수 있습니다.

불완전 타입 , cv 한정자 void* 를 제외한 불완전 타입에 대한 포인터나 참조 , 그리고 rvalue 참조 타입 (C++11부터) 은 예외 명세에서 허용되지 않습니다. 배열과 함수 타입이 사용된 경우 해당 포인터 타입으로 조정되며, 최상위 cv 한정자도 제거됩니다. 매개변수 팩 은 허용됩니다 (C++11부터) .

조정된 타입 집합이 비어 있는 동적 예외 명세 (모든 팩이 확장된 후) (C++11부터) 는 non-throwing입니다. non-throwing 동적 예외 명세를 가진 함수는 어떤 예외도 허용하지 않습니다.

동적 예외 명세(dynamic exception specification)는 함수의 타입에 속하지 않습니다.

함수가 해당 예외 명세에 나열되지 않은 유형의 예외를 던지면, std::unexpected 함수가 호출됩니다. 기본 함수는 std::terminate 를 호출하지만, 사용자가 제공한 함수( std::set_unexpected 를 통해)로 대체될 수 있으며, 이 함수는 std::terminate 를 호출하거나 예외를 던질 수 있습니다. std::unexpected 에서 던져진 예외가 예외 명세에 의해 허용되면, 스택 풀기가 일반적으로 계속됩니다. 허용되지 않지만 예외 명세가 std::bad_exception 을 허용하는 경우, std::bad_exception 이 던져집니다. 그렇지 않으면, std::terminate 가 호출됩니다.

인스턴스화

함수 템플릿 특수화의 동적 예외 명세는 함수 선언과 함께 인스턴스화되지 않으며, 오직 필요할 때 (아래에 정의된 대로)에만 인스턴스화됩니다.

암시적으로 선언된 특별 멤버 함수의 동적 예외 명세는 필요할 때만 평가됩니다 (특히, 파생 클래스의 멤버 함수를 암시적으로 선언하는 경우 기본 멤버 함수의 예외 명세를 인스턴스화할 필요가 없습니다).

함수 템플릿 특수화의 동적 예외 명세가 필요한 경우, 아직 인스턴스화되지 않았다면, 종속 이름들을 조회하고 expression 에 사용된 모든 템플릿들은 특수화의 선언을 위한 것처럼 인스턴스화됩니다.

함수의 동적 예외 명세는 다음 상황에서 필요한 것으로 간주됩니다:

  • 표현식에서 함수가 오버로드 해결을 통해 선택되는 경우
  • 함수가 odr-used 되는 경우
  • 함수가 odr-used될 것이지만 평가되지 않은 피연산자에 나타나는 경우
template<class T>
T f() throw(std::array<char, sizeof(T)>);
int main()
{
    decltype(f<void>()) *p; // f는 평가되지 않지만 예외 명세가 필요함
                            // 예외 명세의 인스턴스화가 sizeof(void)를 계산하기 때문에 오류 발생
}
  • 명세는 다른 함수 선언과 비교하기 위해 필요합니다 (예: 가상 함수 재정의자 또는 함수 템플릿의 명시적 특수화에서)
  • 함수 정의 내에서
  • 기본 설정된 특수 멤버 함수가 자신의 예외 명세를 결정하기 위해 이를 확인해야 하므로 명세가 필요합니다 (이는 기본 설정된 특수 멤버 함수의 명세 자체가 필요한 경우에만 발생합니다).

잠재적 예외

각 함수 f , 함수 포인터 pf , 그리고 멤버 함수 포인터 pmf 가능한 예외 집합 을 가지며, 이는 발생할 수 있는 예외 타입들로 구성됩니다. 모든 타입의 집합은 어떤 예외든 발생할 수 있음을 나타냅니다. 이 집합은 다음과 같이 정의됩니다:

1) f , pf , 또는 pmf 의 선언이 모든 예외를 허용하지 않는 동적 예외 사양을 사용하는 경우 (C++11 이전) , 해당 집합은 그 사양에 나열된 타입들로 구성됩니다.
2) 그렇지 않고, f , pf , 또는 pmf 의 선언이 noexcept(true) 를 사용하는 경우, 집합은 비어 있습니다.
(since C++11)
3) 그렇지 않으면, 해당 집합은 모든 타입의 집합입니다.

참고: 암시적으로 선언된 특별 멤버 함수들(생성자, 대입 연산자, 소멸자) 그리고 상속 생성자들 (C++11부터) 의 경우, 잠재적 예외 집합은 이들이 호출할 모든 것들의 잠재적 예외 집합의 조합입니다: 비정적 데이터 멤버(non-variant), 직접 기반 클래스, 그리고 적절한 경우 가상 기반 클래스들의 생성자/대입 연산자/소멸자들 (언제나 그렇듯 기본 인자 표현식들 포함).

각 표현식 e 잠재적 예외 집합 을 가집니다. 이 집합은 e 핵심 상수 표현식 인 경우 비어 있으며, 그렇지 않으면 e 의 모든 직접적인 하위 표현식들( 기본 인수 표현식 포함)의 잠재적 예외 집합들의 합집합에, e 의 형태에 따라 달라지는 또 다른 집합을 결합한 것입니다:

1) 만약 e 가 함수 호출 표현식인 경우, g 를 호출되는 함수, 함수 포인터, 또는 멤버 함수 포인터라고 하면,
  • 만약 g 의 선언이 동적 예외 명세를 사용하는 경우, g 의 잠재적 예외 집합이 해당 집합에 추가됩니다;
  • 만약 g 의 선언이 noexcept(true) 를 사용하는 경우, 해당 집합은 비어 있습니다;
(C++11부터)
  • 그렇지 않으면, 해당 집합은 모든 타입의 집합입니다.
2) 만약 e 가 함수를 암시적으로 호출하는 경우(연산자 표현식이고 연산자가 오버로드된 경우, new-expression 이고 할당 함수가 오버로드된 경우, 또는 완전 표현식이고 임시 객체의 소멸자가 호출되는 경우), 해당 집합은 그 함수의 집합입니다.
3) 만약 e throw-expression 인 경우, 이 집합은 해당 피연산자로 초기화될 예외이거나 (피연산자가 없는) 재발생 throw-expression의 모든 타입 집합입니다.
4) 만약 e 가 다형적 타입에 대한 참조로의 dynamic_cast 라면, 예외 집합은 std::bad_cast 로 구성됩니다.
5) 만약 e 가 다형성 타입에 대한 역참조된 포인터에 적용된 typeid 라면, 집합은 std::bad_typeid 로 구성됩니다.
6) 만약 e 가 비상수 배열 크기를 갖는 new-expression 이고, 선택된 할당 함수가 비어 있지 않은 잠재적 예외 집합을 갖는 경우, 그 집합은 std::bad_array_new_length 로 구성됩니다.
(since C++11)
void f() throw(int); // f()의 집합은 "int"입니다
void g();            // g()의 집합은 모든 타입의 집합입니다
struct A { A(); };                  // "new A"의 집합은 모든 타입의 집합입니다
struct B { B() noexcept; };         // "B()"의 집합은 비어 있습니다
struct D() { D() throw (double); }; // new D의 집합은 모든 타입의 집합입니다

암시적으로 선언된 모든 멤버 함수 및 상속 생성자 (C++11부터) 는 다음과 같이 선택된 예외 명세를 가집니다:

  • 잠재적 예외 집합이 모든 타입의 집합인 경우, 암시적 예외 명세는 모든 예외를 허용합니다 (예외 명세가 코드로 표현할 수 없고 예외 명세가 없는 것처럼 동작하더라도 예외 명세가 존재하는 것으로 간주됨) (until C++11) noexcept ( false ) 입니다 (since C++11) .
  • 그렇지 않고 잠재적 예외 집합이 비어 있지 않은 경우, 암시적 예외 명세는 해당 집합의 모든 타입을 나열합니다.
  • 그렇지 않은 경우, 암시적 예외 명세는 throw ( ) (until C++11) noexcept ( true ) (since C++11) 입니다.
struct A
{
    A(int = (A(5), 0)) noexcept;
    A(const A&) throw();
    A(A&&) throw();
    ~A() throw(X);
};
struct B
{
    B() throw();
    B(const B&) = default; // 예외 명세는 "noexcept(true)"입니다
    B(B&&, int = (throw Y(), 0)) noexcept;
    ~B() throw(Y);
};
int n = 7;
struct D : public A, public B
{
    // std​::​bad_array_new_length 타입의 핸들러와 일치하는 타입의 예외를 던질 수 있지만,
    // bad allocation 예외는 던지지 않습니다
    (void*) new (std::nothrow) int[n];
    // D는 다음과 같은 암시적으로 선언된 멤버들을 가질 수 있습니다:
    // D::D() throw(X, std::bad_array_new_length);
    // D::D(const D&) noexcept(true);
    // D::D(D&&) throw(Y);
    // D::~D() throw(X, Y);
};

참고 사항

Clang은 C++11에서 동적 예외 사양(dynamic exception specification)의 인스턴스화 규칙이 CWG1330 에 의해 변경되었다고 간주합니다. 자세한 내용은 LLVM #56349 를 참조하십시오.

키워드

throw

예제

참고: 경고를 피하기 위해 C++98 모드에서 컴파일하는 것이 좋습니다. C++17 및 이후 개정판과 호환되지 않습니다.

#include <cstdlib>
#include <exception>
#include <iostream>
class X {};
class Y {};
class Z : public X {};
class W {};
void f() throw(X, Y) 
{
    bool n = false;
    if (n)
        throw X(); // OK, would call std::terminate()
    if (n)
        throw Z(); // also OK
    throw W(); // will call std::unexpected()
}
void handler()
{
    std::cerr << "That was unexpected!\n"; // flush needed
    std::abort();
}
int main()
{
    std::set_unexpected(handler);
    f();
}

출력:

That was unexpected!

결함 보고서

다음 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.

DR 적용 대상 게시된 동작 올바른 동작
CWG 25 C++98 다른 예외 명세를 가진 멤버 포인터 간의
대입 및 초기화 동작이 명시되지 않음
함수 포인터와 참조에 대한
제한 사항 적용
CWG 973 C++98 예외 명세에 함수 타입을 포함할 수 있지만
해당 함수 포인터 변환이 명시되지 않음
명시됨
CWG 1330 C++98 예외 명세가 즉시 인스턴스화될 수 있음 필요한 경우에만 인스턴스화됨
CWG 1267 C++11 예외 명세에서 rvalue 참조 타입이 허용됨 허용되지 않음
CWG 1351 C++98
C++11
기본 인수(C++98)와 기본 멤버 초기화자
(C++11)가 암시적 예외 명세에서 무시됨
고려하도록 변경
CWG 1777 C++11 throw ( T... ) 는 팩이 비어 있어도
비예외 명세가 아님
팩이 비어 있으면
비예외 명세임
CWG 2191 C++98 typeid 표현식의 잠재적 예외 집합에
발생할 수 없는 경우에도 bad_typeid 가 포함될 수 있음
발생할 수 있는 경우에만
bad_typeid 포함

참고 항목

noexcept specifier (C++11) 함수가 예외를 발생시킬 수 있는지 여부를 지정합니다