콘텐츠로 이동

noexcept

유용하고 올바른 경우에 noexcept를 지정하세요.

noexcept 지정자는 함수가 예외를 던질지 여부를 지정합니다. noexcept로 표시된 함수에서 예외가 빠져나가면 프로그램은 std::terminate로 종료됩니다.

noexcept 연산자는 표현식이 예외를 던지지 않는다고 선언되었는지를 컴파일 타임에 검사하여 참/거짓을 돌려줍니다.

  • 이동 생성자를 noexcept로 지정하면 성능이 향상되는 경우가 있습니다. 예를 들어 T의 이동 생성자가 noexcept이면 std::vector<T>::resize()는 객체를 복사하지 않고 이동합니다.
  • 함수에 noexcept를 지정하면 예외가 활성화된 환경에서도 컴파일러 최적화가 가능합니다. 예외가 발생할 수 없음을 알면 스택 되감기(stack unwinding)용 코드를 생성하지 않아도 됩니다.

  • 예외가 비활성화된(이 가이드를 따르는) 프로젝트에서는 noexcept가 올바른지 확인하기 어렵고, 무엇이 "올바름"인지 정의하기도 어렵습니다.

  • 한번 붙인 noexcept를 되돌리기는 어렵습니다. 호출자가 의존할 수 있는 보장을, 감지하기 어려운 방식으로 제거하기 때문입니다.

함수의 의도된 의미를 정확히 반영하고(즉 함수 본문에서 예외가 빠져나가면 그것이 치명적 오류를 뜻하고) 성능에 유용할 때 noexcept를 쓸 수 있습니다. 이동 생성자의 noexcept는 의미 있는 성능 이점이 있다고 가정해도 좋습니다. 다른 함수에 noexcept를 지정해 상당한 성능 이점이 있다고 판단되면 프로젝트 리드와 논의하세요.

예외가 완전히 비활성화된 환경(대부분의 Google C++ 환경)에서는 무조건 noexcept를 선호합니다. 그 외에는, 함수가 예외를 던질 수 있는 소수의 경우에만 false가 되도록 간단한 조건과 함께 조건부 noexcept를 쓰세요. 이때 타입 특성(type trait) 검사를 활용할 수 있습니다(예: 이동 생성에는 std::is_nothrow_move_constructible, 표준 기본 할당자에는 absl::default_allocator_is_nothrow). 대부분의 경우 예외의 유일한 원인은 할당 실패이며(이동 생성자는 할당 실패가 아니라면 예외를 던지지 않아야 한다고 봅니다), 메모리 고갈을 복구 가능한 예외가 아니라 치명적 오류로 처리하는 것이 적절한 프로그램이 많습니다. 다른 실패 가능성도, 가능한 모든 예외 시나리오를 지원하기보다 인터페이스 단순성을 우선하세요. 예컨대 해시 함수가 던질 수 있는지에 따라 복잡한 noexcept 절을 쓰는 대신, "이 컴포넌트는 해시 함수가 예외를 던지는 것을 지원하지 않는다"고 문서화하고 무조건 noexcept로 만드세요.


이해하기 쉽게 설명하기

핵심: 유용하고 올바를 때 noexcept

noexcept는 "이 함수는 예외를 던지지 않는다"는 약속입니다. 어기면(예외가 빠져나가면) 프로그램이 std::terminate로 즉시 종료됩니다.

왜 붙이나?

  • 성능: 이동 생성자가 noexcept이면 std::vector::resize() 같은 연산이 복사 대신 이동을 선택해 빨라집니다.
  • 최적화: 컴파일러가 스택 되감기 코드를 생략할 수 있습니다.

어떻게 붙이나?

  • 예외가 완전히 꺼진 환경(대부분의 Google 환경) → 무조건 noexcept 선호.
  • 그 외 → 던질 수 있는 소수 경우에만 false가 되는 조건부 noexcept. 타입 특성(std::is_nothrow_move_constructible 등)을 활용.
  • 복잡해지면, 가능한 모든 예외를 지원하려 애쓰지 말고 "예외를 던지는 것을 지원하지 않음"을 문서화하고 무조건 noexcept로.

주의

한번 붙인 noexcept는 되돌리기 어렵습니다(호출자가 그 보장에 의존하게 되므로). 이동 생성자는 성능 이점이 분명하지만, 다른 함수는 이점이 크다고 판단될 때 프로젝트 리드와 논의하세요.