- Overview
-
Table of Contents
- Special Member Functions: Constructors, Destructors, and the Assignment Operator
- Operator Overloading
- Memory Management
- Templates
- Namespaces
- Time and Date Library
- Streams
- Object-Oriented Programming and Design Principles
- The Standard Template Library (STL) and Generic Programming
- Exception Handling
- Runtime Type Information (RTTI)
- Signal Processing
- Creating Persistent Objects
- Bit Fields
- New Cast Operators
- Environment Variables
- Variadic Functions
- Pointers to Functions
- Function Objects
- Pointers to Members
- Lock Files
- Design Patterns
- Dynamic Linking
- Tips and Techniques
- Five Things You Need to Know About C++11 Unions
- A Tour of C99
- A Tour of C1X
-
C++0X: The New Face of Standard C++
- Reference Wrapper
- The Performance Technical Report
- auto for the People
- Ironing Templates' Syntactic Wrinkles
- Visual C++ Becomes ISO Compliant
- A Garbage Collector for C++
- C99 Core Features in C++0X
- The <code>shared_ptr</code> Class
- The shared_ptr Class, II
- Lambda Expressions and Closures, Part I
- Lambda Expressions and Closures, Part II
- Lambda Expressions and Closures, Part III
- The Type Traits Library, Part I
- The Type Traits Library, Part II
- The Type Traits Library, Part III
- finally Revisited
- The Any Library
- The nullptr Keyword Proposal
- Delegating Constructors
- The Explicit Conversion Operators Proposal
- Conditionally-Supported Behavior
- The weak ptr Class Template, Part I
- The weak ptr Class Template, Part II
- POD Types Revisited
- The rvalue Reference Proposal, Part I
- The rvalue Reference Proposal, Part II
- Proposal for New String Algorithms
- Concepts, Part I
- Concepts, Part II
- constexpr: Generalized Constant Expressions
- The <u>constexpr</u> Proposal: Constructors
- Strongly-Typed enum Types
- C++09: The Road Ahead
- C++09: Proposals by Statuses
- Changing Undefined Behavior to Diagnosable Errors
- New Character Types
- The __func__ Predeclared Identifier is Coming to C++
- Static Assertions
- The extern template Proposal
- Variadic Templates, Part I
- Variadic Templates, Part II
- Variadic Templates, Part III -- Critique
- Using unique_ptr, Part I
- Using unique_ptr, Part II
- Unrestricted Unions, Part I
- Unrestricted Unions, Part II
- Unrestricted Unions, Part III
- Types With No Linkage as Template Arguments
- New Initialization Syntax
- Initializer Lists and Sequence Constructors
- New Standard Library Algorithms
- Class Member Initializers
- Inheriting Constructors
- Introducing Attributes
- The Removal of Concepts From C++0x
- The Future of C++0x, Part I
- The Future of C++0X, Part II
- The Debate About Attributes, Part I
- The Debate About Attributes, Part II
- The Debate About Attributes, Part III
- The Debate About Attributes, Part IV
- Forward Declarations of Enum Types
- The SCARY Iterators Proposal, Part I
- The SCARY Iterators Proposal, Part II
- Heading for Deprecation: <tt>export</tt>, Exception Specification and <tt>register</tt>
- The Rejection of the Unified Function Syntax Proposal
- Rvalue References as Object Members
- FCD Approved
- The Debate on noexcept, Part I
- The Debate on noexcept, Part II
- The Debate on noexcept, Part III
- About-face -- [[Attributes]] to Be Replaced with Keywords
- Will Delegating Constructors Be Removed From C++0x?
- Rvalue References: Past, Present and Future, Part I
- Rvalue References: Past, Present and Future, Part II
- Rvalue References: Past, Present and Future, Part III
- A Move in the Right Direction, Part I
- A Move in the Right Direction, Part II
- New Keywords for Inheritance Control, Part I
- New Keywords for Inheritance Control, Part II
- FDIS Approved
- C++0x Concurrency
- The Reflecting Circle
- We Have Mail
- The Soapbox
- Numeric Types and Arithmetic
- Careers
- Locales and Internationalization
The Debate on noexcept, Part I
Last updated Jan 1, 2003.
At the Pittsburgh meeting in March 2010, the ISO C++ committee added the new keyword noexcept to the FCD. The precise semantics of noexcept is still a subject for debates. Here I will outline the reasons for adding noexcept, contrast it with traditional exceptions specifications that were just deprecated, and introduce the main points of controversy.
Exception Specifications
There's been a long standing consensus that the C++98 exception specifications are no good. There are several problems with them, most of which have been discussed in length previously. I'll briefly summarize them again.
Dynamic checking. Exception specifications are checked at runtime, not at compile-time. Consequently, programmers calling a function with a certain exception specification can't be sure that the exception specification will be honored at runtime. Furthermore, an implementation must generate additional code to perform the runtime checking of exceptions. That leads to inefficient code and hampers certain optimizations.
Generic Code. It's difficult to write accurate exception specifications in generic code because the designer can't tell what types of exceptions may be thrown from operations on template arguments.
The vast experience of the last two decades shows that in practice, only two forms of exceptions specifications are useful:
- The lack of an overt exception specification, which designates a function that can throw any type of exception:
int func(); //might throw any exception
int add(int, int) nothrow; //never throws
In practice, most functions that never throw don't include an overt throw() specification because exception specifications have fallen from grace, and because a throw() specification incurs performance overhead (I'll discuss the overhead shortly).
These facts are known to the majority of C++ programmers. It's no wonder then that the committee decided in its last meeting to deprecate exception specifications.
Note: The traditional exception specifications in the form
int f() throw(x,y...);
are now referred to as "dynamic exception specifications", to make them distinct from the newly added keyword noexcept, which serves as a compile-time exception specification.
Why Not Stop At Deprecation?
If the committee had settled for deprecating dynamic exception specifications, very few eyebrows would have been raised. However, some members felt that a mechanism for indicating a function that never throws was necessary (especially in move operations) as an optimization cue to the compiler. At this point you're probably wondering: "isn't that what the throw() exception specification does anyway?" Yes, but not exactly. Indeed throw() indicates that a function doesn't throw. However, suppose that a function declared throw() violates its exception specification and throws an exception:
void f() (const char * text) throw();
{
std::string s(text);
}
Although f() doesn't have an overt throw statement, its throw() specification is misleading because the constructor of std::string might throw. What should happen in that case? The C++98 standard requires that the implementation shall unwind the stack of the offending function (invoke destructors of local objects, flush I/O streams etc) and then invoke the Standard Library function std::unexpected(). unexpected() may try (at least in theory) to recover from the exception, which is why an implementation must ensure proper unwinding of the stack. That "proper unwinding of the stack" is what inhibits optimizations of functions declared throw().
In contrast, noexcept implies no stack unwinding. Furthermore, whereas a dynamic exception specification is checked at runtime, the noexcept keyword is checked at compile-time, when possible. Although the compiler can't detect every violation of noexcept, it will issue a diagnostic if it can determine that a function declared noexcept might throw, as in the following example:
void g() noexcept
{
throw Myexception("disaster!"); //C++0x compilation error/warning
}
I believe that at this stage of the C++0x standardization process, the best approach would have been to stop at the deprecation of dynamic exception specifications. We know they're bad and we don't use them anyway. It's too late now to invent a new exception specification mechanism just days before the approval of the FCD. However, that's exactly what happened in Pittsburgh -- the new keyword noexcept was added to the FCD, causing controversy (the minutes from that meeting are available here).
How Does noexcept Differ from throw()?
The proposal doesn't provide all the details with respect to the semantic differences between throw() and noexcept. Generally speaking, the latter is (supposedly) a compile-time specification, as opposed to the former which is checked at runtime. Practically, that means two things:
A compiler may be able to apply certain optimizations to a function declared noexcept. Those optimizations are disabled by default because a function that's not declared noexcept might throw any exception type.
Additionally, the proposal requires that in the event of a noexcept violation, the implementation shall call terminate() (instead of unexpected()) More specifically, the implementation shall not invoke local objects' destructors if a noexcept violation has occurred.
The Debate on terminate()
The requirement to invoke terminate() in the event of a noexcept violation started a debate: Why not state that noexcept violations causes undefined behavior? After all, a noexcept violation shouldn't occur, and if it does, it shouldn't be different from other instances of bad programming practice, say deleting the same pointer twice, buffer overflows etc. Recall that undefined behavior means that anything can happen, including what appears to be normal execution flow.
Another practical argument against calling terminate() was raised. A number of domain areas, such as embedded programming, require terminate() to never be called, insisting that an application should continue to run forever. Therefore, it may be best to let each implementation decide how to handle noexcept violations instead of prescribing that terminate() shall be called.
However, other members strongly opposed to the idea of allowing a program to continue executing past the noexcept call because the results of continuing the program's execution without unwinding the stack might be disastrous -- leaked locks, corrupted files etc. Furthermore, programmers writing code for a catch clause would not be able to distinguish a "valid exception" from an exception that has violated noexcept and therefore couldn't tell how to handle an exception at all.
In the next part of this series I will discuss the arguments in favor of, and against invoking terminate() in depth, and try to predict what will come out of this proposal.
