예외 스펙 (C++ 전용)

C++는 지정된 함수가 지정된 예외 목록만 처리하도록 제한되도록 하는 메커니즘을 제공합니다. 함수의 시작 부분에 있는 예외 스펙은 함수가 예외 스펙에 포함된 예외만 처리하도록 함수의 호출자에게 보증하는 역할을 합니다.

예를 들어, 함수는 다음과 같습니다.
void translate() throw(unknown_word,bad_grammar) { /* ... */ }

유형이 unknown_word 또는 bad_grammar인 예외 오브젝트 또는 unknown_word 또는 bad_grammar에서 파생된 유형만 처리하도록 명시적으로 지정합니다.

예외 스펙 구문

구문 다이어그램 읽기시각적 구문 다이어그램 건너뛰기 throw ( type_id_list )
type_id_list 는 쉼표로 구분된 유형 목록입니다. 이 목록에서 불완전한 유형, 불완전한 유형에 대한 포인터 또는 참조, 추상 클래스 유형, void에 대한 포인터 이외의 ""rvalue 참조 유형 ( const 및/또는 volatile로 선택적으로 규정됨) 을 지정할 수 없습니다. 예외 스펙에 유형을 정의할 수 없습니다.

예외 스펙이 없는 함수는 모든 예외를 허용합니다. 비어 있는 type_id_list, throw()가 있는 예외 스펙이 있는 함수는 예외 처리를 허용하지 않습니다.

예외 스펙은 함수 유형의 일부가 아닙니다.

예외 스펙은 함수의 함수 선언자 끝, 함수에 대한 포인터, 함수에 대한 참조, 멤버 함수 선언에 대한 포인터 또는 멤버 함수 정의에 대한 포인터에만 나타날 수 있습니다. 예외 스펙은 typedef 선언에 표시될 수 없습니다. 다음 선언은 이를 보여줍니다.
  void f() throw(int);
  void (*g)() throw(int);
  void h(void i() throw(int));
  // typedef int (*j)() throw(int);  This is an error.
컴파일러가 마지막 선언 typedef int (*j)() throw(int)을 (를) 허용하지 않습니다.
A 클래스가 함수의 예외 스펙의 type_id_list 에 있는 유형 중 하나라고 가정하십시오. 해당 함수는 A클래스의 예외 오브젝트 또는 A클래스에서 공개적으로 파생된 모든 클래스를 처리할 수 있습니다. 다음 예는 이를 보여줍니다.
class A { };
class B : public A { };
class C { };

void f(int i) throw (A) {
   switch (i) {
      case 0: throw A();
      case 1: throw B();
      default: throw C();
   }
}

void g(int i) throw (A*) {
   A* a = new A();
   B* b = new B();
   C* c = new C();
   switch (i) {
      case 0: throw a;
      case 1: throw b;
      default: throw c;
   }
}
함수 f()A 또는 B유형의 오브젝트를 처리할 수 있습니다. 함수가 C유형의 오브젝트를 처리하려고 시도하는 경우, 컴파일러는 unexpected() 를 호출합니다. C 유형이 함수의 예외 스펙에 지정되지 않았거나 A에서 공개적으로 파생되지 않기 때문입니다. 마찬가지로, 함수 g()C유형의 오브젝트에 대한 포인터를 처리할 수 없습니다. 함수는 A 유형의 포인터 또는 A에서 공용으로 파생된 오브젝트의 포인터를 처리할 수 있습니다.
가상 함수를 대체하는 함수는 가상 함수에서 지정한 예외만 처리할 수 있습니다. 다음 예는 이를 보여줍니다.
class A {
   public:
      virtual void f() throw (int, char);
};

class B : public A{
   public: void f() throw (int) { }
};

/* The following is not allowed. */
/*
   class C : public A {
      public: void f() { }
   };

   class D : public A {
      public: void f() throw (int, char, double) { }
   };
*/
멤버 함수가 int유형의 예외만 처리할 수 있으므로 컴파일러가 B::f() 를 허용합니다. 멤버 함수가 모든 유형의 예외를 처리할 수 있으므로 컴파일러는 C::f() 를 허용하지 않습니다. 멤버 함수가 A::f()보다 더 많은 유형의 예외 (int, chardouble) 를 처리할 수 있으므로 컴파일러가 D::f() 를 허용하지 않습니다.
이름이 y인 함수 또는 함수에 대한 포인터를 사용하여 이름이 x 인 함수에 포인터를 지정하거나 초기화한다고 가정합니다. 함수 x 에 대한 포인터는 y의 예외 스펙에 의해 지정된 예외만 처리할 수 있습니다. 다음 예는 이를 보여줍니다.
void (*f)();
void (*g)();
void (*h)() throw (int);

void i() {
   f = h;
//   h = g;  This is an error.
}
컴파일러는 f = h 지정을 허용합니다. f 가 모든 유형의 예외를 처리할 수 있기 때문입니다. 컴파일러는 h = g 지정을 허용하지 않습니다. hint유형의 오브젝트만 처리할 수 있는 반면, g 는 모든 유형의 예외를 처리할 수 있기 때문입니다.
내재적으로 선언된 특수 멤버 함수 (기본 생성자, 복사 생성자, 소멸자 및 복사 지정 연산자) 에는 예외 스펙이 있습니다. 내재적으로 선언된 특수 멤버 함수는 예외 스펙에서 특수 함수가 호출하는 함수의 예외 스펙에 선언된 유형을 갖습니다. 특수 함수가 호출하는 함수가 모든 예외를 허용하는 경우 해당 특수 함수는 모든 예외를 허용합니다. 특수 함수가 호출하는 모든 함수가 예외를 허용하지 않는 경우 해당 특수 함수는 예외를 허용하지 않습니다. 다음 예는 이를 보여줍니다.
class A {
   public:
      A() throw (int);
      A(const A&) throw (float);
      ~A() throw();
};

class B {
   public:
      B() throw (char);
      B(const A&);
      ~B() throw();
};

class C : public B, public A { };
위의 예에서 다음 특수 함수가 내재적으로 선언되었습니다.
C::C() throw (int, char);
C::C(const C&);   // Can throw any type of exception, including float
C::~C() throw();
C 의 기본 생성자는 int 또는 char유형의 예외를 처리할 수 있습니다. C 의 복사 생성자는 모든 종류의 예외를 처리할 수 있습니다. C 의 소멸자는 예외를 처리할 수 없습니다.