noexcept
Exception SpecificationIt can be helpful both to users and to the compiler to know that a function will not throw any exceptions. Knowing that a function will not throw simplifies the task of writing code that calls that function. Moreover, if the compiler knows that no exceptions will be thrown, it can (sometimes) perform optimizations that must be suppressed if code might throw.
Under the new standard, a function can specify that it does not throw exceptions by providing a noexcept
specification. The keyword noexcept
following the function parameter list indicates that the function won’t throw:
void recoup(int) noexcept; // won't throw
void alloc(int); // might throw
These declarations say that recoup
will not throw any exceptions and that alloc
might. We say that recoup
has a nonthrowing specification.
The noexcept
specifier must appear on all of the declarations and the corresponding definition of a function or on none of them. The specifier precedes a trailing return (§ 6.3.3, p. 229). We may also specify noexcept
on the declaration and definition of a function pointer. It may not appear in a typedef
or type alias. In a member function the noexcept
specifier follows any const
or reference qualifiers, and it precedes final, override
, or = 0
on a virtual function.
It is important to understand that the compiler does not check the noexcept
specification at compile time. In fact, the compiler is not permitted to reject a function with a noexcept
specifier merely because it contains a throw
or calls a function that might throw (however, kind compilers will warn about such usages):
// this function will compile, even though it clearly violates its exception specification
void f() noexcept // promises not to throw any exception
{
throw exception(); // violates the exception specification
}
As a result, it is possible that a function that claims it will not throw will in fact throw. If a noexcept
function does throw, terminate
is called, thereby enforcing the promise not to throw at run time. It is unspecified whether the stack is unwound. As a result, noexcept
should be used in two cases: if we are confident that the function won’t throw, and/or if we don’t know what we’d do to handle the error anyway.
Specifying that a function won’t throw effectively promises the callers of the nonthrowing function that they will never need to deal with exceptions. Either the function won’t throw, or the whole program will terminate; the caller escapes responsibility either way.
The compiler in general cannot, and does not, verify exception specifications at compile time.
noexcept
SpecificationThe noexcept
specifier takes an optional argument that must be convertible to bool:
If the argument is true
, then the function won’t throw; if the argument is false
, then the function might throw:
void recoup(int) noexcept(true); // recoup won't throw
void alloc(int) noexcept(false); // alloc can throw
noexcept
OperatorArguments to the noexcept
specifier are often composed using the noexcept
operator. The noexcept
operator is a unary operator that returns a bool
rvalue constant expression that indicates whether a given expression might throw. Like sizeof
(§ 4.9, p. 156), noexcept
does not evaluate its operand.
For example, this expression yields true
:
noexcept(recoup(i)) // true if calling recoup can't throw, false otherwise
because we declared recoup
with a noexcept
specifier. More generally,
noexcept(e)
is true
if all the functions called by e
have nonthrowing specifications and e
itself does not contain a throw
. Otherwise, noexcept(e)
returns false
.
We can use the noexcept
operator to form an exception specifier as follows:
void f() noexcept(noexcept(g())); // f has same exception specifier as g
If the function g
promises not to throw, then f
also is nonthrowing. If g
has no exception specifier, or has an exception specifier that allows exceptions, then f
also might throw.
noexcept
has two meanings: It is an exception specifier when it follows a function’s parameter list, and it is an operator that is often used as the bool
argument to a noexcept
exception specifier.
Although the noexcept
specifier is not part of a function’s type, whether a function has an exception specification affects the use of that function.
A pointer to function and the function to which that pointer points must have compatible specifications. That is, if we declare a pointer that has a nonthrowing exception specification, we can use that pointer only to point to similarly qualified functions. A pointer that specifies (explicitly or implicitly) that it might throw can point to any function, even if that function includes a promise not to throw:
// both recoup and pf1 promise not to throw
void (*pf1)(int) noexcept = recoup;
// ok: recoup won't throw; it doesn't matter that pf2 might
void (*pf2)(int) = recoup;
pf1 = alloc; // error: alloc might throw but pf1 said it wouldn't
pf2 = alloc; // ok: both pf2 and alloc might throw
If a virtual function includes a promise not to throw, the inherited virtuals must also promise not to throw. On the other hand, if the base allows exceptions, it is okay for the derived functions to be more restrictive and promise not to throw:
class Base {
public:
virtual double f1(double) noexcept; // doesn't throw
virtual int f2() noexcept(false); // can throw
virtual void f3(); // can throw
};
class Derived : public Base {
public:
double f1(double); // error: Base::f1 promises not to throw
int f2() noexcept(false); // ok: same specification as Base::f2
void f3() noexcept; // ok: Derived f3 is more restrictive
};
When the compiler synthesizes the copy-control members, it generates an exception specification for the synthesized member. If all the corresponding operation for all the members and base classes promise not to throw, then the synthesized member is noexcept
. If any function invoked by the synthesized member can throw, then the synthesized member is noexcept(false)
. Moreover, if we do not provide an exception specification for a destructor that we do define, the compiler synthesizes one for us. The compiler generates the same specification as it would have generated had it synthesized the destructor for that class.