A unary operator that determines whether the evaluation of its operand can propagate an exception. Note that the bodies of called functions are not examined, so noexcept
can yield false negatives. The operand is not evaluated.
#include <iostream>
#include <stdexcept>
void foo() { throw std::runtime_error("oops"); }
void bar() {}
struct S {};
int main() {
std::cout << noexcept(foo()) << '\n'; // prints 0
std::cout << noexcept(bar()) << '\n'; // prints 0
std::cout << noexcept(1 + 1) << '\n'; // prints 1
std::cout << noexcept(S()) << '\n'; // prints 1
}
In this example, even though bar()
can never throw an exception, noexcept(bar())
is still false because the fact that bar()
cannot propagate an exception has not been explicitly specified.
When declaring a function, specifies whether or not the function can propagate an exception. Alone, it declares that the function cannot propagate an exception. With a parenthesized argument, it declares that the function can or cannot propagate an exception depending on the truth value of the argument.
void f1() { throw std::runtime_error("oops"); }
void f2() noexcept(false) { throw std::runtime_error("oops"); }
void f3() {}
void f4() noexcept {}
void f5() noexcept(true) {}
void f6() noexcept {
try {
f1();
} catch (const std::runtime_error&) {}
}
In this example, we have declared that f4
, f5
, and f6
cannot propagate exceptions. (Although an exception can be thrown during execution of f6
, it is caught and not allowed to propagate out of the function.) We have declared that f2
may propagate an exception. When the noexcept
specifier is omitted, it is equivalent to noexcept(false)
, so we have implicitly declared that f1
and f3
may propagate exceptions, even though exceptions cannot actually be thrown during the execution of f3
.
Whether or not a function is noexcept
is part of the function's type: that is, in the example above, f1
, f2
, and f3
have different types from f4
, f5
, and f6
. Therefore, noexcept
is also significant in function pointers, template arguments, and so on.
void g1() {}
void g2() noexcept {}
void (*p1)() noexcept = &g1; // ill-formed, since g1 is not noexcept
void (*p2)() noexcept = &g2; // ok; types match
void (*p3)() = &g1; // ok; types match
void (*p4)() = &g2; // ok; implicit conversion