Exception specifiers were deprecated because exception specifiers are generally a terrible idea. noexcept
was added because it's the one reasonably useful use of an exception specifier: knowing when a function won't throw an exception. Thus it becomes a binary choice: functions that will throw and functions that won't throw.
noexcept
was added rather than just removing all throw specifiers other than throw()
because noexcept
is more powerful. noexcept
can have a parameter which compile-time resolves into a boolean. If the boolean is true, then the noexcept
sticks. If the boolean is false, then the noexcept
doesn't stick and the function may throw.
Thus, you can do something like this:
struct<typename T>
{
void CreateOtherClass() { T t{}; }
};
Does CreateOtherClass
throw exceptions? It might, if T
's default constructor can. How do we tell? Like this:
struct<typename T>
{
void CreateOtherClass() noexcept(is_nothrow_default_constructible<T>::value) { T t{}; }
};
Thus, CreateOtherClass()
will throw iff the given type's default constructor throws. This fixes one of the major problems with exception specifiers: their inability to propagate up the call stack.
You can't do this with throw()
.
noexcept
may incur runtime checks. The main difference between them being that breakingnoexcept
causesstd::terminate
while breakingthrow
causesstd::unexpected
. Also a slightly different stack unwinding behaviour in these cases. – Durian