In this post I would like to share my observation on where using noexcept really adds value. It is less often than what one might expect, and it does not have that much to do with throwing or not throwing exceptions. The conclusion surprises me a bit, and I hesitate to present it because it is counter to the advice I hear from people I consider authorities on the subject.

Semantics of noexcept

What does it mean to the program that a function is declared as noexcept ? Forget what the intent of the programmer is, or could be. Just answer a ‘technical’ question: what does it change in the program from the perspective of run-time behaviour, observable results and the type system?

Two most important things:

It turns an exception throw into a call to std::terminate() . One can make a compile-time query if the function has been declared as noexcept .

This can be illustrated with the following mean “Hello World” program:

// DON'T DO IT AT HOME! void printMessage() { cout << "Hello World!" << endl; exit(0); } void execute() noexcept { if (noexcept(execute())) throw "Ha!"; } int main() { set_terminate(&printMessage); execute(); }

This is the opposite of any good programming practice, but it illustrates my point well: the intent of putting a feature into the language is one thing and the formal semantics of the feature is another.

There is one other effect of noexcept , although less interesting for this post. Calling std::terminate() in the situation above does not require performing stack unwinding, or the stack can be only partially unwound.

Why do you need to know?

Is this an important information for you if a given function may throw or not? If so, why? Some possible answers include the following:

Because if it throws and I do not catch it, std::terminate will be called and I do not want this to happen. Because I need this function to provide no-throw exception safety guarantee. Because this may allow certain compiler optimizations.

Did I miss anything?

If your motivation is (1), noexcept buys you nothing. While you know that no exception will be propagated out, std::terminate() is called for you even sooner!

If your motivation is (2), noexcept will also not help you much. Suppose you detect that the function is not declared as noexcept , what do you do? Don’t use it? It may still not throw exceptions. A function can throw nothing and still be declared noexcept(false) . This is the case for the std::swap specialization for STL containers: following the recommendation from N3248 it is guaranteed not to throw, but is declared noexcept(false) . For this reason, the example I gave in my other post with function nofail_swap is wrong. It doesn’t take into account that swap on STL containers is no-fail. You cannot check the no-fail or no-throw guarantee with a compile-time expression, because in some cases it is just announced informally in the documentation.

Also, if a function is declared noexcept it can’t throw, but it can call std::terminate . Do you consider this behaviour suitable for a component that is supposed to be “exception safe”?

If your motivation is (3), then there is a chance that you will gain some performance, but not as much as you think (if any). Compiler can perform certain no-throw optimizations even without your help (especially for inline functions). And annotating anything with noexcept comes with certain costs: the risk of calling std::terminate and making function declarations more obscure. C++ allows you to annotate functions wit many information, but when you try to use all of it, function declarations can get really long. Consider these:

constexpr inline int const& getX() const noexcept;

or:

virtual inline int const& getY() const noexcept override [[carries_dependency]];

So unless you are determined you want to have performance squeezed of these noexcept declarations and you have measured that they actually speed the program up rather than slow it down, I would consider it a premature optimization.

So, what is it for?

Given this negative attitude to noexcept , can it be considered useful at all? Yes. The noexcept feature was introduced very late into C++11 to address one particular issue with move semantics. It has been described here by Douglas Gregor and David Abrahams. In short, for certain functions, like vector<T>::push_back using move constructors/assignments of T instead of copy constructors/assignments can dramatically increase performance. However, if this move constructor/assignment can potentially throw, push_back would loose the strong exception safety guarantee. In order to take an advantage of the move operations (where possible) and at the same time keep the strong guarantee, there needs to be a way to determine if a given move operation is potentially throwing or not, and use it or alternatively fall back to good old copying. This is exactly what function std::move_if_noexcept does, but it needs T ’s operations to be marked as noexcept where appropriate.

But when is it appropriate, given that a move operation declared as noexcept may call std::terminate ? (Note that this is the case for std::thread .) The answer is: you (the author of class T ) decide. If you want the extra performance from vector<T>::push_back at the risk of loosing the strong guarantee, just add it. You are not risking much if you are ‘sure’ your move operations do not throw. The other part of the guarantee (that std::terminate is called upon violating noexcept ) is not even necessary here. Only that you are able to declare your choice and that function overload resolution is able to read your choice. In fact, initially the violation of noexcept was proposed to be an undefined behavior.

So, in other words, the only convincing (for me) usage of noexcept is to declare to function overload resolution mechanism your resolution of the trade-off between run-time performance and exception safety guarantees. Performance obtained this way can be orders of magnitude greater than noexcept -based code generation optimizations. Now, I am talking only about annotating move constructors and move assignments this way. This technique could be applied to any function, but so far, I haven’t seen it used for anything else but move operations.

Note that in the description above no throwing of exceptions is mentioned.

It is no-fail, not no-throw

Now, consider the following somewhat unusual definition of a move assignment.

int Tool::operator=(Tool&& rhs) { if (ResourceHandle tmp = getSentinelResource()) { dispose(this->resourceHandle); this->resourceHandle = rhs.resourceHandle; rhs.resourceHandle = tmp; return EXIT_SUCCESS; } else { return EXIT_FAILURE; } }

Nothing in this code throws exceptions. We use a 3rd party library which does not throw exceptions. We do not throw exceptions ourselves: instead we signal failure using the return value.

Now, imagine we are holding our class Tool in an std::vector . Do we want to speed up the operations by using moves instead of copies? Since move assignment can fail, the same argument from N2855 still holds, the strong exception safety would be compromised. We do not want that, so we need to signal to the overload resolution mechanism that for our class Tool we want the copies to be used rather than moves. Therefore our move assignment must not be declared as noexcept even though we guarantee that we do not throw exceptions! This is because the information that noexcept really is intended to convey is that the function never fails; not that it never throws! We can see above that a function can fail but still not throw, but it still qualifies for noexcept(false) . Perhaps the keyword should have been called nofail . The never-fail guarantee cannot be checked by the compiler (much like any other failure-safety guarantee), therefore the only thing we can do is to declare it.

This is part of a more general observation, that what we are interested in is really failure safety in program components rather than exception safety. No matter if you use exceptions, error return values, errno or whatever else, the reasoning about basic (no leak, invariant preserved), strong (commit or rollback) and never-fail guarantee should still hold.

More eager termination

There is one other usage of noexcept (tied to exceptions for a change) that I can think of, but never needed in practice. This is to call std::terminate more often in hope of finding some failure safety bugs sooner in the application development process.

To illustrate what I mean, consider destructors. If an exception is thrown from a destructor during stack unwinding std::terminate is called. If this is your design decision that your destructors should throw, calling std::terminate is an acceptable and desired consequence. On the other hand, if you are trying to apply the best practice widely accepted in C++ community that destructors should not throw, calling std::terminate in this case is an indication that you inadvertently did throw from a destructor. In that case it is a bug, but one that occurs very seldom. Suppose that you throw from a destructor 1 in 1000 times. Even if you do, it is again 1 in a 1000 times that your exception is called due to stack unwinding. So the probability of calling std::terminate due to double-exception situation is 1/1000000 (assuming the two events are independent), and is likely not to occur during testing, and only in production. On the other hand, if the destructor is marked as noexcept , std::terminate is going to be called whenever an exception is thrown from destructor, during stack unwinding or normal scope exit. So the probability of the throw from destructor being revealed is 1/1000 instead of 1/1000000, and it becomes more likely that we will see it when the program is still in testing stage. And when it happens during testing, the provision that the stack need not be unwound proves very beneficial, because std::terminate can be called directly at the point of the throw and we can precisely identify the culprit.

For this reason destructors are implicitly declared noexcept in C++11. Is this a convincing use case? Answer yourself.