noexcept
specifier
(since C++11)
함수가 예외를 던질 수 있는지 여부를 지정합니다.
목차 |
구문
noexcept
|
(1) | ||||||||
noexcept(
expression
)
|
(2) | ||||||||
throw()
|
(3) |
(C++17에서 사용 중단됨)
(C++20에서 제거됨) |
|||||||
noexcept(true)
와 동일함
(
다음에 오는
noexcept
는 항상 이 형식의 일부입니다(절대로 초기화자를 시작할 수 없습니다).
| expression | - | bool 타입으로 문맥상 변환된 상수 표현식 |
설명
|
noexcept 명세는 함수 타입의 일부가 아니며 (마치 동적 예외 명세 와 같이) 함수, 함수 포인터 타입의 변수, 함수 포인터 타입의 비정적 데이터 멤버, 함수 참조, 멤버 함수 포인터를 선언할 때 람다 선언자 또는 최상위 함수 선언자 의 일부로만 나타날 수 있습니다. 또한 그러한 선언들 중 함수 포인터나 함수 참조인 매개변수나 반환 타입을 선언할 때에도 나타날 수 있습니다. typedef 또는 타입 별칭 선언에서는 나타날 수 없습니다. void f() noexcept; // 함수 f()는 예외를 던지지 않음 void (*fp)() noexcept(false); // fp는 예외를 던질 수 있는 함수를 가리킴 void g(void pfa() noexcept); // g는 예외를 던지지 않는 함수 포인터를 취함 // typedef int (*pf)() noexcept; // 오류 |
(C++17까지) |
|
noexcept 명세는 함수 타입의 일부이며 모든 함수 선언자 의 일부로 나타날 수 있습니다. |
(C++17부터) |
C++의 모든 함수는 non-throwing 이거나 potentially throwing 입니다:
- potentially-throwing 함수는 다음과 같습니다:
|
(C++17까지) |
-
-
noexcept지정자로 선언된 함수 중 expression 이false로 평가되는 경우 -
다음을 제외하고
noexcept지정자 없이 선언된 함수들
-
- destructors (단, 잠재적으로 생성된 기반 또는 멤버의 소멸자가 potentially-throwing 인 경우는 제외 - 아래 참조)
- default constructors , copy constructors , move constructors (암시적으로 선언되거나 첫 선언에서 기본값으로 지정된 경우, 다음 상황 제외)
-
- 생성자의 암시적 정의가 호출할 기반 또는 멤버의 생성자가 potentially-throwing 인 경우 (아래 참조)
- 기본 인수 표현식과 같은 이러한 초기화의 하위 표현식이 potentially-throwing 인 경우 (아래 참조)
- 기본 멤버 초기화자(기본 생성자만 해당)가 potentially-throwing 인 경우 (아래 참조)
- copy assignment operators, move assignment operators (암시적으로 선언되거나 첫 선언에서 기본값으로 지정된 경우, 단 암시적 정의에서 할당 연산자의 호출이 potentially-throwing 인 경우 제외 - 아래 참조)
-
|
(C++20부터) |
-
non-throwing 함수는 다른 모든 함수(noexcept 지정자를 가지며 그
expression
이
true로 평가되는 함수들과 destructor, defaulted special member function, deallocation function을 포함)입니다
명시적 인스턴스화 는 noexcept 지정자를 사용할 수 있지만 필수는 아닙니다. 사용할 경우, 예외 명세는 다른 모든 선언과 동일해야 합니다. 진단은 단일 번역 단위 내에서 예외 명세가 동일하지 않은 경우에만 필요합니다.
예외 명세만 다른 함수들은 오버로드할 수 없습니다 (반환 타입과 마찬가지로, 예외 명세는 함수 타입의 일부이지만 함수 시그니처의 일부는 아닙니다) (C++17부터) .
void f() noexcept; void f(); // 오류: 다른 예외 명세 void g() noexcept(false); void g(); // ok, g에 대한 두 선언 모두 예외 발생 가능
예외를 던지지 않는 함수에 대한 포인터(멤버 함수 포인터 포함)는 할당되거나 초기화에 사용될 수 있습니다 (C++17까지) 예외를 던질 수 있는 함수 포인터로 암시적으로 변환됩니다 (C++17부터) 하지만 그 반대는 불가능합니다.
void ft(); // 예외를 발생시킬 수 있는 함수 void (*fn)() noexcept = ft; // 오류
가상 함수가 non-throwing인 경우, 모든 오버라이더의 모든 선언(정의 포함)은 해당 오버라이더가 삭제된 것으로 정의된 경우를 제외하고 non-throwing이어야 합니다:
struct B { virtual void f() noexcept; virtual void g(); virtual void h() noexcept = delete; }; struct D: B { void f(); // 잘못된 형식: D::f는 예외를 던질 수 있으나, B::f는 예외를 던지지 않음 void g() noexcept; // 정상 void h() = delete; // 정상 };
예외를 던지지 않는 함수는 예외를 던질 수 있는 함수를 호출하는 것이 허용됩니다. 예외가 던져지고 핸들러 검색이 예외를 던지지 않는 함수의 가장 바깥쪽 블록에 도달할 때마다, std::terminate 함수가 호출됩니다:
extern void f(); // 예외를 발생시킬 수 있는 함수 void g() noexcept { f(); // f가 예외를 발생시켜도 유효함 throw 42; // 유효함, 효과적으로 std::terminate 호출 }
함수 템플릿 특수화의 예외 명세는 함수 선언과 함께 인스턴스화되지 않으며, 오직 필요할 때 (아래에 정의된 대로)에만 인스턴스화됩니다.
암시적으로 선언된 특수 멤버 함수의 예외 명세(exception-specification) 역시 필요할 때만 평가됩니다 (특히, 파생 클래스의 멤버 함수를 암시적으로 선언할 때 기반 멤버 함수의 예외 명세를 인스턴스화할 필요가 없습니다).
함수 템플릿 특수화의 noexcept 지정자가 필요한 경우, 아직 인스턴스화되지 않았다면, 종속 이름들을 조회하고 expression 에 사용된 모든 템플릿들은 해당 특수화의 선언을 위한 것처럼 인스턴스화됩니다.
함수의 noexcept 명세는 다음 상황에서 필요하다 고 간주됩니다:
- 표현식에서 함수가 오버로드 해결을 통해 선택되는 경우
- 함수가 odr-used 되는 경우
- 함수가 odr-used될 수 있지만 평가되지 않은 피연산자에 나타나는 경우
template<class T> T f() noexcept(sizeof(T) < 4); int main() { decltype(f<void>()) *p; // f는 평가되지 않지만 noexcept 사양이 필요함 // noexcept 사양의 인스턴스화 과정에서 // sizeof(void)를 계산하기 때문에 오류 발생 }
- 명세는 다른 함수 선언과 비교하기 위해 필요합니다 (예: 가상 함수 재정의자 또는 함수 템플릿의 명시적 특수화에서)
- 함수 정의 내에서
- 기본 제공 특수 멤버 함수가 자체 예외 명세를 결정하기 위해 이를 확인해야 하므로 명세가 필요합니다 (이는 기본 제공 특수 멤버 함수의 명세 자체가 필요한 경우에만 발생합니다).
잠재적으로 예외를 던지는 표현식 의 공식 정의 (위에서 설명한 소멸자, 생성자, 그리고 대입 연산자의 기본 예외 명세를 결정하는 데 사용됨):
표현식
e
가 다음 조건을 만족할 때
potentially-throwing
입니다:
-
e는 예외를 발생시킬 수 있는 함수, 함수 포인터, 또는 멤버 함수 포인터에 대한 함수 호출입니다 , 단e가 핵심 상수 표현식 인 경우는 제외 (C++17까지) -
e가 예외를 발생시킬 수 있는 함수에 대한 암시적 호출을 수행하는 경우 (예: 오버로드된 연산자,new-표현식의 할당 함수, 함수 인수의 생성자, 또는e가 완전 표현식인 경우의 소멸자) -
e가throw-표현식 인 경우 -
e가 다형 참조 타입을 변환하는dynamic_cast인 경우 -
e가 다형 타입에 대한 역참조 포인터에 적용된typeid표현식인 경우 -
e가 예외를 발생시킬 수 있는 직접적인 하위 표현식을 포함하는 경우
struct A { A(int = (A(5), 0)) noexcept; A(const A&) noexcept; A(A&&) noexcept; ~A(); }; struct B { B() throw(); B(const B&) = default; // 암시적 예외 명세는 noexcept(true)입니다 B(B&&, int = (throw Y(), 0)) noexcept; ~B() noexcept(false); }; int n = 7; struct D : public A, public B { int * p = new int[n]; // D::D()는 new 연산자 때문에 잠재적으로 예외를 발생시킬 수 있음 // D::D(const D&)는 예외를 발생시키지 않음 // D::D(D&&)는 잠재적으로 예외를 발생시킬 수 있음: B의 생성자 기본 인자가 예외를 발생시킬 수 있음 // D::~D()는 잠재적으로 예외를 발생시킬 수 있음 // 참고: 만약 A::~A()가 가상 함수였다면, 이 프로그램은 오류일 것입니다. 왜냐하면 // 예외를 발생시키지 않는 가상 함수를 재정의하는 함수가 잠재적으로 예외를 발생시킬 수 없기 때문입니다 };
참고 사항
상수
expression
의 용도 중 하나는 (
noexcept
operator
와 함께) 특정 타입에 대해서는
noexcept
를 선언하고 다른 타입에 대해서는 선언하지 않는 함수 템플릿을 정의하는 것입니다.
함수에 대한
noexcept
지정자는 컴파일 타임 검사가 아니라는 점에 유의하십시오; 이것은 함수가 예외를 던져야 하는지 여부를 프로그래머가 컴파일러에 알리는 방법일 뿐입니다. 컴파일러는 이 정보를 사용하여 예외를 던지지 않는 함수에 대해 특정 최적화를 활성화할 수 있을 뿐만 아니라
noexcept
operator
를 활성화할 수 있으며, 이 연산자는 특정 표현식이 어떤 예외도 던지도록 선언되었는지 컴파일 타임에 확인할 수 있습니다. 예를 들어,
std::vector
와 같은 컨테이너들은 요소들의 이동 생성자가
noexcept
인 경우 요소들을 이동시키고, 그렇지 않으면 복사합니다 (복사 생성자가 접근 불가능하지만 예외를 던질 수 있는 이동 생성자는 접근 가능한 경우는 제외하며, 이 경우 강력한 예외 보장이 포기됩니다).
사용 중단
noexcept
는 C++11에서 폐기된
throw
(
)
의 개선된 버전입니다. C++17 이전의
throw
(
)
와 달리,
noexcept
는
std::unexpected
를 호출하지 않으며, 스택 풀기(stack unwinding)를 수행할 수도 있고 하지 않을 수도 있고,
std::terminate
를 호출합니다. 이로 인해 컴파일러가
noexcept
를
throw
(
)
의 런타임 오버헤드 없이 구현할 수 있게 됩니다. C++17부터
throw
(
)
는
noexcept
(
true
)
와 완전히 동등하게 재정의됩니다.
| 기능 테스트 매크로 | 값 | 표준 | 기능 |
|---|---|---|---|
__cpp_noexcept_function_type
|
201510L
|
(C++17) | 예외 명세를 타입 시스템의 일부로 만들기 |
키워드
noexcept , throw (C++17부터) (C++20까지)
예제
// whether foo is declared noexcept depends on if the expression // T() will throw any exceptions template<class T> void foo() noexcept(noexcept(T())) {} void bar() noexcept(true) {} void baz() noexcept { throw 42; } // noexcept is the same as noexcept(true) int main() { foo<int>(); // noexcept(noexcept(int())) => noexcept(true), so this is fine bar(); // fine baz(); // compiles, but at runtime this calls std::terminate }
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| CWG 1330 | C++11 | 예외 명세가 즉시 인스턴스화될 수 있었음 | 필요한 경우에만 인스턴스화됨 |
| CWG 1740 | C++11 | ( 다음에 오는 noexcept 가 초기화자를 시작할 수 있었음 |
noexcept 명세의 일부만 될 수 있음 |
| CWG 2039 | C++11 | 변환 전 표현식만 상수일 것을 요구했음 |
변환도 상수 표현식에서
유효해야 함 |
참고 항목
noexcept
operator
(C++11)
|
표현식이 예외를 발생시키는지 여부를 결정 |
| Dynamic exception specification (until C++17) | 함수가 발생시키는 예외를 지정 (C++11에서 사용 중단됨) |
throw
expression
|
오류를 신호하고 제어를 오류 처리기로 전달 |
|
(C++11)
|
이동 생성자가 예외를 발생시키지 않는 경우 인수를 xvalue로 변환
(function template) |