Namespaces
Variants

noexcept specifier (since C++11)

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)

함수가 예외를 던질 수 있는지 여부를 지정합니다.

목차

구문

noexcept (1)
noexcept( expression ) (2)
throw() (3) (C++17에서 사용 중단됨)
(C++20에서 제거됨)
1) noexcept(true) 와 동일함
2) 만약 expression true 로 평가되면, 해당 함수는 어떤 예외도 던지지 않도록 선언됩니다. ( 다음에 오는 noexcept 는 항상 이 형식의 일부입니다(절대로 초기화자를 시작할 수 없습니다).
3) noexcept(true) 와 동일함 (C++17 이전의 의미론은 dynamic exception specification 참조)
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 지정자 없이 선언된 함수들
  • 생성자의 암시적 정의가 호출할 기반 또는 멤버의 생성자가 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 오류를 신호하고 제어를 오류 처리기로 전달
이동 생성자가 예외를 발생시키지 않는 경우 인수를 xvalue로 변환
(function template)