noexcept 연산자
noexcept 연산자 (C++11)
함수 하나가 예외를 던질 수도 있는 함수인지 아닌지를 컴파일 타임에 미리 확인하고 싶을 때가 있어요. 그럴 때 noexcept 연산자를 쓰면 돼요. 이 연산자는 표현식이 예외를 던지지 않는다고 선언되어 있으면 true를 돌려주는 컴파일 타임 검사를 수행해요.
이 연산자는 함수 템플릿의 noexcept 지정자 안에서 특히 유용해요. 어떤 타입에 대해서는 예외를 던지고, 다른 타입에 대해서는 던지지 않는다고 선언할 수 있거든요.
본문
구문
noexcept(expression)
bool 타입의 prvalue를 돌려줘요. 결과는 표현식의 잠재적 예외 집합이 비어 있으면(until C++17) 표현식이 던지지 않음(non-throwing)으로 지정되어 있으면(since C++17) true, 그렇지 않으면 false예요.
expression은 평가되지 않는 피연산자(unevaluated operand)예요.
expression이 prvalue라면 임시 객체 구체화(temporary materialization)가 적용돼요. (C++17 이후)
참고 (Notes)
noexcept(expr)이 true라고 해도, expr을 평가하다가 정의되지 않은 동작(undefined behavior)을 만나면 여전히 예외가 던져질 수 있다는 점을 기억해 둘게요.
expression이 클래스 타입이거나 (가능하면 다차원) 그 배열이라면, 임시 객체 구체화에는 소멸자가deleted가 아니고 접근 가능해야 해요. (C++17 이후)
키워드
noexcept
예제
noexcept 연산자가 여러 종류의 호출에 대해 어떤 결과를 내는지 확인해 볼게요.
#include <iostream>
#include <utility>
#include <vector>
void may_throw();
void no_throw() noexcept;
auto lmay_throw = []{};
auto lno_throw = []() noexcept {};
class T
{
public:
~T(){} // dtor prevents move ctor
// copy ctor is noexcept
};
class U
{
public:
~U(){} // dtor prevents move ctor
// copy ctor is noexcept(false)
std::vector<int> v;
};
class V
{
public:
std::vector<int> v;
};
int main()
{
T t;
U u;
V v;
std::cout << std::boolalpha <<
"may_throw() is noexcept(" << noexcept(may_throw()) << ")\n"
"no_throw() is noexcept(" << noexcept(no_throw()) << ")\n"
"lmay_throw() is noexcept(" << noexcept(lmay_throw()) << ")\n"
"lno_throw() is noexcept(" << noexcept(lno_throw()) << ")\n"
"~T() is noexcept(" << noexcept(std::declval<T>().~T()) << ")\n"
// note: the following tests also require that ~T() is noexcept because
// the expression within noexcept constructs and destroys a temporary
"T(rvalue T) is noexcept(" << noexcept(T(std::declval<T>())) << ")\n"
"T(lvalue T) is noexcept(" << noexcept(T(t)) << ")\n"
"U(rvalue U) is noexcept(" << noexcept(U(std::declval<U>())) << ")\n"
"U(lvalue U) is noexcept(" << noexcept(U(u)) << ")\n"
"V(rvalue V) is noexcept(" << noexcept(V(std::declval<V>())) << ")\n"
"V(lvalue V) is noexcept(" << noexcept(V(v)) << ")\n";
}
출력:
may_throw() is noexcept(false)
no_throw() is noexcept(true)
lmay_throw() is noexcept(false)
lno_throw() is noexcept(true)
~T() is noexcept(true)
T(rvalue T) is noexcept(true)
T(lvalue T) is noexcept(true)
U(rvalue U) is noexcept(false)
U(lvalue U) is noexcept(false)
V(rvalue V) is noexcept(true)
V(lvalue V) is noexcept(false)
예제를 보면 흥미로운 점이 보여요. noexcept 표현식 안에서 임시 객체를 만들고 파괴하기 때문에, T(rvalue T)와 T(lvalue T)가 모두 true인 건 ~T()가 noexcept이기 때문이에요. 반면 std::vector 멤버를 가진 U는 복사 생성자가 noexcept(false)라서 false가 돼요. V도 std::vector 멤버가 있는데 rvalue로 만들 땐 이동 생성자가 쓰이니 true, lvalue로 만들 땐 복사 생성자가 쓰이니 false가 나와요.
결함 보고 (Defect reports)
다음은 이전에 발행된 C++ 표준에 소급 적용된, 동작을 바꾸는 결함 보고(defect report)예요.
| DR | 적용 대상 | 발행 당시 동작 | 올바른 동작 |
|---|---|---|---|
| CWG 2722 | C++17 | expression이 prvalue일 때 임시 객체 구체화가 적용되는지 불명확했음 |
이 경우 적용됨 |
| CWG 2792 | C++11 | noexcept 연산자가 정의되지 않은 동작을 만났을 때 예외가 던져질 수 있는지 판단해야 했음 |
필요 없음 |
더 알아보기 (Learn more)
noexcept지정자 — 함수가 예외를 던질 수 있는지 지정하는 문법이에요. 연산자와 함께 공부하면 좋아요.- 동적 예외 지정(Dynamic exception specification) — 함수가 어떤 예외를 던지는지 지정하는 옛 방식이에요. C++11에서 폐기 예정(deprecated)이 되었어요.