When should I really use noexcept?
Solution 1
I think it is too early to give a "best practices" answer for this as there hasn't been enough time to use it in practice. If this was asked about throw specifiers right after they came out then the answers would be very different to now.
Having to think about whether or not I need to append
noexcept
after every function declaration would greatly reduce programmer productivity (and frankly, would be a pain).
Well, then use it when it's obvious that the function will never throw.
When can I realistically expect to observe a performance improvement after using
noexcept
? [...] Personally, I care aboutnoexcept
because of the increased freedom provided to the compiler to safely apply certain kinds of optimizations.
It seems like the biggest optimization gains are from user optimizations, not compiler ones due to the possibility of checking noexcept
and overloading on it. Most compilers follow a no-penalty-if-you-don't-throw exception handling method, so I doubt it would change much (or anything) on the machine code level of your code, although perhaps reduce the binary size by removing the handling code.
Using noexcept
in the big four (constructors, assignment, not destructors as they're already noexcept
) will likely cause the best improvements as noexcept
checks are 'common' in template code such as in std
containers. For instance, std::vector
won't use your class's move unless it's marked noexcept
(or the compiler can deduce it otherwise).
Solution 2
As I keep repeating these days: semantics first.
Adding noexcept
, noexcept(true)
and noexcept(false)
is first and foremost about semantics. It only incidentally condition a number of possible optimizations.
As a programmer reading code, the presence of noexcept
is akin to that of const
: it helps me better grok what may or may not happen. Therefore, it is worthwhile spending some time thinking about whether or not you know if the function will throw. For a reminder, any kind of dynamic memory allocation may throw.
Okay, now on to the possible optimizations.
The most obvious optimizations are actually performed in the libraries. C++11 provides a number of traits that allows knowing whether a function is noexcept
or not, and the Standard Library implementation themselves will use those traits to favor noexcept
operations on the user-defined objects they manipulate, if possible. Such as move semantics.
The compiler may only shave a bit of fat (perhaps) from the exception handling data, because it has to take into account the fact that you may have lied. If a function marked noexcept
does throw, then std::terminate
is called.
These semantics were chosen for two reasons:
- immediately benefiting from
noexcept
even when dependencies do not use it already (backward compatibility) - allowing the specification of
noexcept
when calling functions that may theoretically throw, but are not expected to for the given arguments
Solution 3
This actually does make a (potentially) huge difference to the optimizer in the compiler. Compilers have actually had this feature for years via the empty throw() statement after a function definition, as well as propriety extensions. I can assure you that modern compilers do take advantage of this knowledge to generate better code.
Almost every optimization in the compiler uses something called a "flow graph" of a function to reason about what is legal. A flow graph consists of what are generally called "blocks" of the function (areas of code that have a single entrance and a single exit) and edges between the blocks to indicate where flow can jump to. Noexcept alters the flow graph.
You asked for a specific example. Consider this code:
void foo(int x) {
try {
bar();
x = 5;
// Other stuff which doesn't modify x, but might throw
} catch(...) {
// Don't modify x
}
baz(x); // Or other statement using x
}
The flow graph for this function is different if bar
is labeled noexcept
(there is no way for execution to jump between the end of bar
and the catch statement). When labeled as noexcept
, the compiler is certain the value of x is 5 during the baz function - the x=5 block is said to "dominate" the baz(x) block without the edge from bar()
to the catch statement.
It can then do something called "constant propagation" to generate more efficient code. Here if baz is inlined, the statements using x might also contain constants and then what used to be a runtime evaluation can be turned into a compile-time evaluation, etc.
Anyway, the short answer: noexcept
lets the compiler generate a tighter flow graph, and the flow graph is used to reason about all sorts of common compiler optimizations. To a compiler, user annotations of this nature are awesome. The compiler will try to figure this stuff out, but it usually can't (the function in question might be in another object file not visible to the compiler or transitively use some function which is not visible), or when it does, there is some trivial exception which might be thrown that you're not even aware of, so it can't implicitly label it as noexcept
(allocating memory might throw bad_alloc, for example).
Solution 4
noexcept
can dramatically improve performance of some operations. This does not happen at the level of generating machine code by the compiler, but by selecting the most effective algorithm: as others mentioned, you do this selection using function std::move_if_noexcept
. For instance, the growth of std::vector
(e.g., when we call reserve
) must provide a strong exception-safety guarantee. If it knows that T
's move constructor doesn't throw, it can just move every element. Otherwise it must copy all T
s. This has been described in detail in this post.
Solution 5
When can I realistically except to observe a performance improvement after using
noexcept
? In particular, give an example of code for which a C++ compiler is able to generate better machine code after the addition of noexcept.
Um, never? Is never a time? Never.
noexcept
is for compiler performance optimizations in the same way that const
is for compiler performance optimizations. That is, almost never.
noexcept
is primarily used to allow "you" to detect at compile-time if a function can throw an exception. Remember: most compilers don't emit special code for exceptions unless it actually throws something. So noexcept
is not a matter of giving the compiler hints about how to optimize a function so much as giving you hints about how to use a function.
Templates like move_if_noexcept
will detect if the move constructor is defined with noexcept
and will return a const&
instead of a &&
of the type if it is not. It's a way of saying to move if it is very safe to do so.
In general, you should use noexcept
when you think it will actually be useful to do so. Some code will take different paths if is_nothrow_constructible
is true for that type. If you're using code that will do that, then feel free to noexcept
appropriate constructors.
In short: use it for move constructors and similar constructs, but don't feel like you have to go nuts with it.
void-pointer
Updated on July 08, 2022Comments
-
void-pointer almost 2 years
The
noexcept
keyword can be appropriately applied to many function signatures, but I am unsure as to when I should consider using it in practice. Based on what I have read so far, the last-minute addition ofnoexcept
seems to address some important issues that arise when move constructors throw. However, I am still unable to provide satisfactory answers to some practical questions that led me to read more aboutnoexcept
in the first place.There are many examples of functions that I know will never throw, but for which the compiler cannot determine so on its own. Should I append
noexcept
to the function declaration in all such cases?Having to think about whether or not I need to append
noexcept
after every function declaration would greatly reduce programmer productivity (and frankly, would be a pain in the neck). For which situations should I be more careful about the use ofnoexcept
, and for which situations can I get away with the impliednoexcept(false)
?When can I realistically expect to observe a performance improvement after using
noexcept
? In particular, give an example of code for which a C++ compiler is able to generate better machine code after the addition ofnoexcept
.Personally, I care about
noexcept
because of the increased freedom provided to the compiler to safely apply certain kinds of optimizations. Do modern compilers take advantage ofnoexcept
in this way? If not, can I expect some of them to do so in the near future?
-
R. Martinho Fernandes almost 12 yearsCode that uses
move_if_nothrow
(or whatchamacallit) will see a performance improvement if there's a noexcept move ctor. -
moooeeeep about 6 years
-
TamaMcGlinn about 3 years
-
Jonathan Wakely almost 12 yearsStrictly,
move_if_noexcept
won't return a copy, it will return a const lvalue-reference rather than an rvalue-reference. In general that will cause the caller to make a copy instead of a move, butmove_if_noexcept
isn't doing the copy. Otherwise, great explanation. -
mfontanini almost 12 years+1 Jonathan. Resizing a vector, for example, will move the objects instead of copying them if the move constructor is
noexcept
. So that "never" is not true. -
Nicol Bolas almost 12 years@mfontanini: That's not a compiler optimization though, which is what he was asking about.
-
Matthieu M. almost 12 yearsI think the
std::terminate
trick still obeys the Zero-Cost model. That is, it just so happens that the range of instructions within thenoexcept
functions is mapped to callstd::terminate
ifthrow
is used instead of the stack unwinder. I therefore doubt it has more overhead that regular exception tracking. -
mfontanini almost 12 yearsI mean, the compiler will generate better code in that situation. OP is asking for an example for which the compiler is able to generate a more optimized application. This seems to be the case(even though it's not a compiler optimization).
-
Admin almost 12 yearsMaybe I'm naive, but I would imagine a function that invokes only
noexcept
functions wouldn't need to do anything special, because any exceptions that might arise triggerterminate
before they get to this level. This differs greatly from having to deal with and propagate abad_alloc
exception. -
Nicol Bolas almost 12 years@mfontanini: The compiler only generates better code because the compiler is forced to compile a different codepath. It only works because
std::vector
is written to force the compiler to compile different code. It's not about the compiler detecting something; it's about user code detecting something. -
Matthieu M. almost 12 years@Hurkyl: you are right. Which is why a sufficiently smart compiler might figure this out and avoid generating entries in the program-counter/exception-handler table for this function (thus reducing the fat as I said). However many functions will not be marked
noexcept
(think about system calls for example, or low-level OS-specific C functions), which is why the short-cut chosen was that the compiler did not have to check it. -
mfontanini almost 12 years"give an example of code for which a C++ compiler is able to generate better machine code after the addition of noexcept" -> I know it's not the compilers fault that the application does things more efficienttly, but it's the compiler who generates it after all.
-
Christian Rau almost 12 yearsAnd since the library is a specified part of the C++ standard, I don't really care if it was the compiler or the library, as long as tagging my move constructor
noexcept
gives faster code across all implementations. -
Nicol Bolas almost 12 years@mfontanini: "it's the compiler who generates it after all." By that logic, using a better algorithm is a compiler optimization. Anything is a compiler optimization. Thus the term has no useful meaning.
-
mfontanini almost 12 yearsThe thing is, I can't seem to find "compiler optimization" in the quote at the begining of your answer. As @ChristianRau said, it the compiler generates a more efficient code, it doesn't matter what's the origin of that optimization. After all, the compiler is generating a more efficient code, isn't it? PS: I never said it was a compiler optimization, I even said "It's not a compiler optimization".
-
Klaim almost 12 years"For instance, std::vector won't use your class's move unless it's marked noexcept." What? Really? Are you sure it is required?
-
Pubby almost 12 years@Klaim See this: stackoverflow.com/a/10128180/964135 Actually it just has to be non-throwing, but the
noexcept
guarantees this. -
Klaim almost 12 years@Pubby Ok thanks for the clarification, that's quite an interesting subtlety. That explains why my code still does moves.
-
Potatoswatter almost 12 years"If a
noexcept
function throws thenstd::terminate
is called which seems like it would involve a small amount of overhead"… No, this should be implemented by not generating exception tables for such a function, which the exception dispatcher should catch and then bail out. -
Potatoswatter almost 12 yearsDoes this actually make a difference in practice? The example is contrived because nothing before
x = 5
can throw. If that part of thetry
block served any purpose the reasoning wouldn't hold. -
Pubby almost 12 years@Potatoswatter Yeah, I guess the point I was trying to make is that it won't remove the exception handling completely. Have a suggestion to what I should change that sentence in the post to?
-
Potatoswatter almost 12 years@Pubby C++ exception handling is usually done with no overhead except jump tables which map potentially throwing call site addresses to handler entry points. Removing those tables is as close as it gets to removing exception handling completely. The only difference is executable file size. Probably not worth mentioning anything.
-
Terry Mahaffey almost 12 yearsI'd say it does make a real difference in optimizing functions which contain try/catch blocks. The example I gave although contrived, isn't exhaustive. The larger point is that noexcept (like the throw() statement before it) helps the compile generate a smaller flow graph (less edges, less blocks) which is a fundamental part of many optimizations it then does.
-
tr3w about 11 yearsYes, it is possible to define noexcept in way as you suggest, but that would be a really unusable feature. Many function can throw if certain conditions aren't hold, and you couldn't call them even if you know the conditions are met. For example any function which may throw std::invalid_argument.
-
Matthieu M. about 11 years@tr3w: I understand your argument, however I will oppose real-world practice => it's so easy to misunderstand the requirement and have the function throw that it should not be allowed to be in a
noexcept
function (without proper try/catch at least). And even if now it works, any number of refactoring/maintenance may invalidate this later on. That's the biggest advantage of compile-time checks: they are exhaustive and hold version after version. -
bartolo-otrit about 11 yearsWhy they don't do noexcept as a default argument?
-
supercat almost 11 yearsSince compiler changes often require that everything be rebuilt anyway, why not use name mangling so declaring a method
foo_whatever
asnoexcept
would define bothfoo_whatever
and__noex__foo_whatever
, while defining a method withoutnoexcept
would just define the former. If anoexcept
methods callsfoo
, the compiler should have it call__noex_foo_whatever
and also generate a weak definition for__noex_foo_whatever
which callsfoo_whatever
and terminates if it throws an exception? Then anoexcept
which only calls othernoexcept
methods could avoid all overhead. -
Honf over 10 years@MatthieuM. A bit late for a reply, but nevertheless. Functions marked noexcept can call other functions that can throw, the promise is that this function will not emit an exception i.e. they just have to handle the exception themselves!
-
Steve Jessop over 10 yearsOr pass arguments that ensure the callee won't throw even though it does for other inputs -- something that compilers aren't smart enough or don't have enough information to check, but programmers on a good day are and do. You don't always want to have to write "except" and "noexcept" versions of your functions according to whether the caller has somehow pre-validated the inputs, like
operator[]
vs.at()
. -
Nemo over 9 years"Well then use it when it's obvious that the function will never throw." I disagree.
noexcept
is part of the function's interface; you should not add it just because your current implementation happens not to throw. I am not sure about the right answer to this question, but I am quite confident that how your function happens to behave today has nothing to do with it... -
Nemo over 9 years@Potatoswatter: "C++ exception handling is usually done with no overhead except jump tables" -- Not true according to some compiler authors; e.g. search for "Chandler" at meetingcpp.com/index.php/br/items/insights-into-new-and-c.html. The compiler knowing that control flow is linear can certainly enable some optimizations, at least in principle.
-
Potatoswatter over 9 years@Nemo See also my recent answer, stackoverflow.com/questions/26079903/…
-
Nemo over 9 yearsI upvoted this answer a long time ago, but having read and thought about it some more, I have a comment/question. "Move semantics" is the only example I have ever seen anybody give where
noexcept
is clearly helpful / a good idea. I am starting to think move construction, move assignment, and swap are the only cases there are... Do you know of any others? -
Matthieu M. over 9 years@Nemo: In the Standard library, it is possibly the only one, however it exhibits a principle which can be reused elsewhere. A move operation is an operation that temporarily put some state in "limbo", and only when it is
noexcept
may someone use it confidently on a piece of data that could be accessed afterward. I could see this idea being used elsewhere, but the Standard library is rather thin in C++ and it is only used to optimize out copies of elements I think. -
Tomas Kubes about 9 yearsHow can compiler recognize that the code can throw exception? Is and access to array considered as possible exception?
-
cdhowie almost 9 years@qub1n If the compiler can see the body of the function it can look for explicit
throw
statements, or other things likenew
that can throw. If the compiler cannot see the body then it must rely on the presence or absence ofnoexcept
. Plain array access does not typically generate exceptions (C++ doesn't have bounds checking) so no, array access wouldn't alone cause the compiler to think a function throws exceptions. (Out-of-bound access is UB, not a guaranteed exception.) -
mucaho over 8 yearsAddendum: That means if you define move constructors or move assignment operators add
noexcept
to them (if applicable)! Implicitly defined move member functions havenoexcept
added to them automatically (if applicable). -
mip over 8 yearsStill it would make much more sense if exceptions were implemented in C++ like in Java, where you mark method that may throw with
throws
keyword instead ofnoexcept
negative. I just can not get some of C++ design choices... -
AnorZaken almost 8 yearsThis is the best answer by far. You are making a guarantee to users of your method, which is another way of saying you are constraining your implementation forever (sans breaking-change). Thanks for the enlightening perspective.
-
AnorZaken almost 8 yearsThey named it
noexcept
becausethrow
was already taken. Simply putthrow
can be used almost they way you mention, except they botched the design of it so it became almost useless - detrimental even. But we are stuck with it now since removing it would be a breaking change with little benefit. Sonoexcept
is basicallythrow_v2
. -
Christian Rau about 7 years@NicolBolas I feel like you're really arguing semantics here. I know it's not exactly the compiler that generates better code. I also know what you want to say conceptually and that arguing semantics isn't such a bad thing in C++. But let's be honest,
move_if_noexcept
is exactly the kind of stuff the question is looking for. If it is strictly the compiler or the library doesn't matter, since from a practical viewpoint both are the same in this scenario to any user who doesn't actually develop the C++ standard library, which hardly anyone does. -
Raedwald about 6 yearsSee also a related Java question.
-
curiousguy almost 6 years@Nemo Any optimisation possible without exception is still possible with exceptions, it's an obvious observation.
-
Nemo almost 6 years@curiousguy: Wrong. It is trivial to think of examples if you know anything about compiler optimization... But I will just refer you to akrzemi1.wordpress.com/2014/04/24/noexcept-what-for and suggest you do some more reading on your own
-
curiousguy almost 6 years@Nemo The reference does not describe any compiler optimisation.
-
curiousguy almost 6 yearsHow is
throw
not useful? -
Philipp Claßen almost 6 years@curiousguy "throw" itself (for throwing exceptions) is useful, but "throw" as an exception specifier has been deprecated and in C++17 even removed. For reasons why exception specifiers are not useful, see this question: stackoverflow.com/questions/88573/…
-
curiousguy almost 6 years@PhilippClaßen The
throw()
exception specifier did not provide the same guarantee asnothrow
? -
Philipp Claßen almost 6 years@curiousguy Yes, there is a difference that is relevant for compiler optimization. If you do throw,
throw()
enforces that the stack is completely unwound, whereas C++11noexcept
leaves it open to implementation. That is less constraining for the optimizer. See this question, which also contains the quote from Scott Meyer's book that I am referring to: stackoverflow.com/q/26079903/783510 -
curiousguy almost 6 years"That freedom allows further code optimization as it lowers the overhead of always being able to unwind the stack." That should make no difference with a zero overhead implementation.
-
curiousguy almost 6 years@cdhowie "it must rely on the presence or absence of noexcept" or presence of
throw()
in pre-noexcept C++ -
curiousguy almost 6 years@TerryMahaffey "real difference in optimizing functions which contain try/catch blocks" ... that fully handle the exception, not Java "finally"-like blocks that rethrow, which is 99% of try/catch in lib code
-
ar2015 over 5 yearsIs this feature important enough to create a keyword for?
-
Neonit almost 5 yearsThis sounds to me as if I actually should add a
noexcept
every time, except I explicitly want to take care of exceptions. Let's be real, most of the exceptions are so improbable and/or so fatal that rescuing is hardly reasonable or possible. E.g. in the quoted example, if allocation fails, the application will hardly be able to continue working correctly. -
Philipp Claßen over 4 yearsIn practice, there are no zero overhead exception implementations. Herb Sutter has a proposal that could change that: open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0709r0.pdf
-
Philipp Claßen over 4 yearsBut even for stack unwinding when you do not throw, the compiler is restricted. It is additionally limited by calling conventions (part of the ABI). It is not an easy problem for compilers to implement the current exception model without introducing any overhead.
-
HAL9000 over 3 yearsis it still too early?
-
nxh over 3 yearsWhat are the big 4? In your answer I only see 3: constructors, assignment, not destructors. Can someone please elaborate?
-
Ben Voigt about 3 years@NamHoang: 1. Copy constructor 2. Copy assignment 3. Move constructor 4. Move assignment
-
Prasanth Kumar about 3 yearsThis may be a naive question, but why is the focus on
vector<double> tmp(10);
? Can't the string instance creation in the line above equally throw if there's not enough memory for it? -
user643011 almost 3 yearsLooks like you are using this example: youtube.com/watch?v=AG_63_edgUg My question is: Will using
-fno-exceptions
compiler option trigger the same performance benefit as marking the move constructornoexcept
? -
user643011 almost 3 yearsI just tested it with GCC and clang trunk and it seems like marking the function
noexcept
is still required even if using-fno-execptions
for the move constructor to be preferred.