What is the difference between C++03 `throw()` specifier and C++11 `noexcept`?
Asked Answered
S

3

113

Is there any difference between throw() and noexcept other than being checked at runtime and compile time respectively?

This Wikipedia C++11 article suggests that the C++03 throw specifiers are deprecated.
Why so ... Is the noexcept capable enough to cover all that at compile time?


Note: I checked this question and this article, but couldn't determine the solid reason for its deprecation.

Shanda answered 11/10, 2012 at 6:9 Comment(2)
Accodring to this nice article also noexcept may incur runtime checks. The main difference between them being that breaking noexcept causes std::terminate while breaking throw causes std::unexpected. Also a slightly different stack unwinding behaviour in these cases.Durian
There is nothing "compile time" checked with some exception specifications that is "runtime" checked on others. It's just a myth created by opponents of C++ exception specifications.Prang
M
149

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().

Martian answered 11/10, 2012 at 6:23 Comment(17)
+1 Useful answer, for me anyway. Still searching for an answer that says why I would want to use noexcept. I never used throw() specifier, ever and am attempting to determine if noexcept actually provides any benefit (other than compiler checked documentation).Huskey
Just found this #10788266 ...Huskey
@hmjd: "Still searching for an answer that says why I would want to use noexcept." You won't find an answer to that here because that's not the question that was asked.Martian
Yep, I know that. Was just researching noexcept and reading a few questions/answers on it. The most important reason I found was that some (possibly all) of the STL containers will not use the move constructor if it is not declared as noexcept, which I didn't realise.Huskey
I wonder what happened to the case, that destructors shall not call functions that throw exceptions.Danner
@Alex: It's fine for destructors to call throwing functions. They just can't allow those exceptions to escape the destructor call.Martian
@NicolBolas agree. but if noexcept would be a guarantee, the compiler could check whether a function may throw or not in a destructor. Thus being able to warn a programmer that a function is noexcept or not.Danner
@Alex: noexcept is a guarantee. If an exception tries to leave any function that is noexcept, then std::terminate will be called.Martian
@NicolBolas the runtime calls std::terminate. which is WAY WORSE! code can sneak into releases that have functions marked noexcept and at runtime (meaning at customer sites) violations are detected. I meant the compiler guarantees to generate code that doesn't throw exceptions in the first place.Danner
@Alex: C++ is not a safe language. It is always the responsibility of the programmer to ensure the integrity of a C++ program. The same goes here: you are allowed to call functions that aren't marked noexcept. The language trusts that you will either call these functions in such a way that they will not throw, or that you will call these function in such a way that you will catch any exceptions they throw. There is no reason to force C++ programmers to only restrict themselves to functions explicitly labeled noexcept. That would break tons of code.Martian
let us continue this discussion in chatDanner
@NicolBolas: One other difference worth noting. If a function is marked throws() then if an exception is thrown the stack must be unwound upto the scope of that function (so all automatic variables in the function are destroyed) at which point terminate() is called (via unexpected()). If a function is marked noexcept then if an exception is thrown then terminate is called (the unwinding of the stack is implementation defined detail).Shorthanded
@MartinYork A difference in semantics that is irrelevant to the calling code.Prang
@Huskey "The most important reason I found was that some (possibly all) of the STL containers will not use the move constructor if it is not declared as noexcept" Yes and changing the function decoration from throw() to noexcept was not needed to do that.Prang
"Exception specifiers were deprecated because exception specifiers are generally a terrible idea." Then the new style is equally "terrible" as the exact same critics apply, except the less useful "throw some exceptions" have been removed (which was never part of the central argument against old style spec)Prang
@curiousguy: You know, there were several sentences after the first one. Indeed, the very next sentence addresses this exact point. It's almost like I was going somewhere with that. Hence the use of qualifiers like "generally" and so forth.Martian
@NicolBolas They were found to be of little use, but their bad reputation is similar to the bad rep of MI in many languages, it's based on completely irrelevant or bogus claims, bad example, bad understanding... Bad understanding is fixed by better explanations, not changing language syntax and semantics. I'm not saying that the full power of old throw spec was worth keeping given the (relatively extremely small) additional language and impl complexity they caused, but denigration of a tool is bad and awful when it propages deep misunderstanding of programming.Prang
I
35

noexcept isn't checked at compile time.

An implementation shall not reject an expression merely because when executed it throws or might throw an exception that the containing function does not allow.

When a function that is declared noexcept or throw() attempts to throw an exception the only difference is that one calls terminate and the othe calls unexpected and the latter style of exception handling has effectively been deprecated.

Inestimable answered 11/10, 2012 at 6:23 Comment(1)
But if a virtual function has throw()/noexcept, compile time checking ensure that an overrider also has.Prang
V
2

std::unexpected() is called by the C++ runtime when a dynamic exception specification is violated: an exception is thrown from a function whose exception specification forbids exceptions of this type.

std::unexpected() may also be called directly from the program.

In either case, std::unexpected calls the currently installed std::unexpected_handler. The default std::unexpected_handler calls std::terminate.

Vitriolic answered 10/7, 2017 at 17:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.