Home > Articles > Programming > C/C++

C++ Reference Guide

Hosted by

Toggle Open Guide Table of ContentsGuide Contents

Close Table of ContentsGuide Contents

Close Table of Contents

The Debate on noexcept, Part III

Last updated Jan 1, 2003.

In the final part of this series I will summarize the pros and cons and conclude the discussion about the fate of noexcept and its usefulness.

Redundant? Not Quite

In the previous part I raised the "redundancy claim", which essentially suggests that noexcept might be redundant because in some cases at least, the compiler can check a function's code to determine whether it will throw an exception. If the compiler can prove that a function will not throw, it doesn't need noexcept in the first place.

The proponents of noexcept raise an important counter-argument. Indeed, when the compiler processes a function definition it can sometimes tell whether that function will not throw without a noexcept declaration. However, this is true only for functions whose definitions are visible to the compiler. If a certain translation unit contains a call to a function defined in a different translation unit, the compiler has no way of knowing whether that function throws. That's where the noexcept keyword proves to be most useful -- it allows both the programmer and the compiler to tell that a certain function doesn't throw. Looking back at our previous example:

void f2() noexecpt;
void f1() noexcept
{
 f2(); //OK, calling a noexcept function
}

Adding noexcept to the prototype of f2() seemingly resolves the dilemma -- the compiler assumes that the call to f2() will not throw, which means that f1() will not throw either. But what if the programmer who wrote the declaration of f2() with noexcept was wrong? What if the definition of f2() changes in the future, turning f2() into a function that does throw? Will the programmer remember to update all the declarations of f2()? Unlike inline, which can't cause too much harm even if it's wrong, noexcept isn't to be taken lightly -- a noexcept violation can be disastrous. That is why I believe that noexcept is bad idea to begin with. Indeed, it may improve performance in some cases but the risks are too high. The proposal also assumes that the programmer is an exception safety expert, which is rarely the case.

Summarizing the Problems with noexcept

Let's summarize the problems with the current specification of noexcept.

No existing practice. The committee fell again into the well-known trap of "design by committee", standardizing a feature that had never been implemented in the real world. If noexcept had been proposed a couple of years ago, at least we would have had some time to evaluate it, fine-tune it or even scrap it. Alas, the noexcept proposal appeared out of the blue, just days before the FCD's closing date.

Underspecification. The proposal boasts "compile-time checking" but that's false advertising. In some cases, the compiler is supposed to validate the noexcept guarantee. However, there are cases in which the compiler can't do that. Worse yet, the proposal doesn't state which tests the compiler should perform and what's considered a "show-stopper". For example, is it OK to declare a function that might rarely throw noexcept? Should the compiler dig deep into every possible execution path of a noexcept function?

Benchmarking. Thus far, no official benchmark results have been published. It's therefore not clear what the potential performance gain of noexcept is. What if it turns out to be negligible? Will that justify the risks associated with noexcept? Even if the performance improvement is staggering, the results should have been published before the Pittsburgh meeting, not after.

Disagreement about noexcept violations. Although the proposal requires that std::terminate() shall be called if a noexcept function throws, that policy is both controversial and underspecified. Which resources are supposed to be released in such case? Should the program crash unconditionally, causing resource leaks and risking data integrity? I can understand why stack unwinding is ruled out but what about other resources such as locks and system I/O buffers? Letting an airplane crash just because a sloppy programmer forgot to remove noexcept from an old header file doesn't sound reassuring.

Compile-time checking is unrealistic. Let's admit it: unless the C++ standard enforces Whole Program Analysis, it's simply ridiculous to assume that the compiler will apply compile-time checking of noexcept. If the compiler can do that only in certain cases, that's the worst option because programmers will never know whether a successful compilation means that a noexcept function has been validated by the compiler or does that mean that the compiler was unable to validate the noexcept guarantee and therefore trusted the programmer's judgment. We have plenty of experience with register and inline suggesting that programmers' judgment -- as far as optimization is concerned -- doesn't necessarily outwit the compiler's.

In Conclusion

Although noexcept is in the FCD, we still don't have full specifications thereof nor do we have an exhaustive checklist of what's required of the implementation. I believe that as with other questionable C++0x features, noexcept is a recipe for trouble, and that its benefits are offset by its drawbacks. Premature optimization is evil, as we all know too well.